mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-26 10:11:10 -06:00
Merge branch 'lm_gizmos' into lm_fdm_supports_gizmo
This commit is contained in:
commit
a380053a17
227 changed files with 28690 additions and 16849 deletions
|
|
@ -22,6 +22,7 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/ConfigSnapshotDialog.hpp
|
||||
GUI/3DScene.cpp
|
||||
GUI/3DScene.hpp
|
||||
GUI/format.hpp
|
||||
GUI/GLShader.cpp
|
||||
GUI/GLShader.hpp
|
||||
GUI/GLCanvas3D.hpp
|
||||
|
|
@ -30,9 +31,10 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/GLCanvas3DManager.cpp
|
||||
GUI/Selection.hpp
|
||||
GUI/Selection.cpp
|
||||
GUI/Gizmos/GLGizmos.hpp
|
||||
GUI/Gizmos/GLGizmosManager.cpp
|
||||
GUI/Gizmos/GLGizmosManager.hpp
|
||||
GUI/Gizmos/GLGizmosCommon.cpp
|
||||
GUI/Gizmos/GLGizmosCommon.hpp
|
||||
GUI/Gizmos/GLGizmoBase.cpp
|
||||
GUI/Gizmos/GLGizmoBase.hpp
|
||||
GUI/Gizmos/GLGizmoMove.cpp
|
||||
|
|
@ -193,12 +195,16 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
|||
|
||||
encoding_check(libslic3r_gui)
|
||||
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi)
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES})
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (SLIC3R_STATIC AND UNIX AND NOT APPLE)
|
||||
target_compile_definitions(libslic3r_gui PRIVATE OPENSSL_CERT_OVERRIDE)
|
||||
endif ()
|
||||
|
||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -394,6 +394,7 @@ void GLVolume::render() const
|
|||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
||||
#if !ENABLE_SLOPE_RENDERING
|
||||
void GLVolume::render(int color_id, int detection_id, int worldmatrix_id) const
|
||||
{
|
||||
if (color_id >= 0)
|
||||
|
|
@ -409,6 +410,7 @@ void GLVolume::render(int color_id, int detection_id, int worldmatrix_id) const
|
|||
|
||||
render();
|
||||
}
|
||||
#endif // !ENABLE_SLOPE_RENDERING
|
||||
|
||||
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
|
||||
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
|
||||
|
|
@ -535,16 +537,16 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front
|
||||
// edge has y=0 and centerline of the back edge has y=depth:
|
||||
Pointf3s points;
|
||||
std::vector<Vec3crd> facets;
|
||||
std::vector<Vec3i> facets;
|
||||
float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 },
|
||||
{ 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } };
|
||||
int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
|
||||
{8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
|
||||
{0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11} };
|
||||
for (int i = 0; i < 16; ++i)
|
||||
points.push_back(Vec3d(out_points_idx[i][0] / (100.f / min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
|
||||
points.emplace_back(out_points_idx[i][0] / (100.f / min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]);
|
||||
for (int i = 0; i < 28; ++i)
|
||||
facets.push_back(Vec3crd(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]));
|
||||
facets.emplace_back(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]);
|
||||
TriangleMesh tooth_mesh(points, facets);
|
||||
|
||||
// We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to
|
||||
|
|
@ -650,28 +652,64 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
|
||||
GLint z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "z_range") : -1;
|
||||
GLint clipping_plane_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "clipping_plane") : -1;
|
||||
|
||||
GLint print_box_min_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.min") : -1;
|
||||
GLint print_box_max_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.max") : -1;
|
||||
GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
|
||||
GLint print_box_active_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.actived") : -1;
|
||||
GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
GLint slope_active_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.actived") : -1;
|
||||
GLint slope_normal_matrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.volume_world_normal_matrix") : -1;
|
||||
GLint slope_z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.z_range") : -1;
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
glcheck();
|
||||
|
||||
if (print_box_min_id != -1)
|
||||
glsafe(::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min));
|
||||
glsafe(::glUniform3fv(print_box_min_id, 1, (const GLfloat*)m_print_box_min));
|
||||
|
||||
if (print_box_max_id != -1)
|
||||
glsafe(::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max));
|
||||
glsafe(::glUniform3fv(print_box_max_id, 1, (const GLfloat*)m_print_box_max));
|
||||
|
||||
if (z_range_id != -1)
|
||||
glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range));
|
||||
glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)m_z_range));
|
||||
|
||||
if (clipping_plane_id != -1)
|
||||
glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)clipping_plane));
|
||||
glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)m_clipping_plane));
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
if (slope_z_range_id != -1)
|
||||
glsafe(::glUniform2fv(slope_z_range_id, 1, (const GLfloat*)m_slope.z_range.data()));
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
|
||||
for (GLVolumeWithIdAndZ& volume : to_render) {
|
||||
volume.first->set_render_color();
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
if (color_id >= 0)
|
||||
glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)volume.first->render_color));
|
||||
else
|
||||
glsafe(::glColor4fv(volume.first->render_color));
|
||||
|
||||
if (print_box_active_id != -1)
|
||||
glsafe(::glUniform1i(print_box_active_id, volume.first->shader_outside_printer_detection_enabled ? 1 : 0));
|
||||
|
||||
if (print_box_worldmatrix_id != -1)
|
||||
glsafe(::glUniformMatrix4fv(print_box_worldmatrix_id, 1, GL_FALSE, (const GLfloat*)volume.first->world_matrix().cast<float>().data()));
|
||||
|
||||
if (slope_active_id != -1)
|
||||
glsafe(::glUniform1i(slope_active_id, m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower ? 1 : 0));
|
||||
|
||||
if (slope_normal_matrix_id != -1)
|
||||
{
|
||||
Matrix3f normal_matrix = volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>();
|
||||
glsafe(::glUniformMatrix3fv(slope_normal_matrix_id, 1, GL_FALSE, (const GLfloat*)normal_matrix.data()));
|
||||
}
|
||||
|
||||
volume.first->render();
|
||||
#else
|
||||
volume.first->render(color_id, print_box_detection_id, print_box_worldmatrix_id);
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
}
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
|
|
@ -1816,7 +1854,9 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height
|
|||
thick_point_to_verts(point, width, height, volume);
|
||||
}
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
GLModel::GLModel()
|
||||
: m_filename("")
|
||||
|
|
@ -1885,7 +1925,16 @@ void GLModel::render() const
|
|||
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
|
||||
glcheck();
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
if (color_id >= 0)
|
||||
glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_volume.render_color));
|
||||
else
|
||||
glsafe(::glColor4fv(m_volume.render_color));
|
||||
|
||||
m_volume.render();
|
||||
#else
|
||||
m_volume.render(color_id, -1, -1);
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
|
|
@ -1899,7 +1948,7 @@ void GLModel::render() const
|
|||
bool GLArrow::on_init()
|
||||
{
|
||||
Pointf3s vertices;
|
||||
std::vector<Vec3crd> triangles;
|
||||
std::vector<Vec3i> triangles;
|
||||
|
||||
// bottom face
|
||||
vertices.emplace_back(0.5, 0.0, -0.1);
|
||||
|
|
@ -1965,7 +2014,7 @@ GLCurvedArrow::GLCurvedArrow(unsigned int resolution)
|
|||
bool GLCurvedArrow::on_init()
|
||||
{
|
||||
Pointf3s vertices;
|
||||
std::vector<Vec3crd> triangles;
|
||||
std::vector<Vec3i> triangles;
|
||||
|
||||
double ext_radius = 2.5;
|
||||
double int_radius = 1.5;
|
||||
|
|
@ -2099,6 +2148,7 @@ bool GLBed::on_init_from_file(const std::string& filename)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
std::string _3DScene::get_gl_info(bool format_as_html, bool extensions)
|
||||
{
|
||||
return Slic3r::GUI::GLCanvas3DManager::get_gl_info().to_string(format_as_html, extensions);
|
||||
|
|
@ -2133,5 +2183,6 @@ GUI::GLCanvas3D* _3DScene::get_canvas(wxGLCanvas* canvas)
|
|||
{
|
||||
return s_canvas_mgr.get_canvas(canvas);
|
||||
}
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@
|
|||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "slic3r/GUI/GLCanvas3DManager.hpp"
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
|
@ -443,7 +445,9 @@ public:
|
|||
void set_range(double low, double high);
|
||||
|
||||
void render() const;
|
||||
#if !ENABLE_SLOPE_RENDERING
|
||||
void render(int color_id, int detection_id, int worldmatrix_id) const;
|
||||
#endif // !ENABLE_SLOPE_RENDERING
|
||||
|
||||
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
|
||||
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
||||
|
|
@ -479,20 +483,36 @@ public:
|
|||
|
||||
private:
|
||||
// min and max vertex of the print box volume
|
||||
float print_box_min[3];
|
||||
float print_box_max[3];
|
||||
float m_print_box_min[3];
|
||||
float m_print_box_max[3];
|
||||
|
||||
// z range for clipping in shaders
|
||||
float z_range[2];
|
||||
float m_z_range[2];
|
||||
|
||||
// plane coeffs for clipping in shaders
|
||||
float clipping_plane[4];
|
||||
float m_clipping_plane[4];
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
struct Slope
|
||||
{
|
||||
// toggle for slope rendering
|
||||
bool active{ false };
|
||||
// [0] = yellow, [1] = red
|
||||
std::array<float, 2> z_range;
|
||||
};
|
||||
|
||||
Slope m_slope;
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
public:
|
||||
GLVolumePtrs volumes;
|
||||
|
||||
GLVolumeCollection() {};
|
||||
~GLVolumeCollection() { clear(); };
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
GLVolumeCollection() { set_default_slope_z_range(); }
|
||||
#else
|
||||
GLVolumeCollection() = default;
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
~GLVolumeCollection() { clear(); }
|
||||
|
||||
std::vector<int> load_object(
|
||||
const ModelObject *model_object,
|
||||
|
|
@ -543,12 +563,21 @@ public:
|
|||
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }
|
||||
|
||||
void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) {
|
||||
print_box_min[0] = min_x; print_box_min[1] = min_y; print_box_min[2] = min_z;
|
||||
print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z;
|
||||
m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z;
|
||||
m_print_box_max[0] = max_x; m_print_box_max[1] = max_y; m_print_box_max[2] = max_z;
|
||||
}
|
||||
|
||||
void set_z_range(float min_z, float max_z) { z_range[0] = min_z; z_range[1] = max_z; }
|
||||
void set_clipping_plane(const double* coeffs) { clipping_plane[0] = coeffs[0]; clipping_plane[1] = coeffs[1]; clipping_plane[2] = coeffs[2]; clipping_plane[3] = coeffs[3]; }
|
||||
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
|
||||
void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; }
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
bool is_slope_active() const { return m_slope.active; }
|
||||
void set_slope_active(bool active) { m_slope.active = active; }
|
||||
|
||||
const std::array<float, 2>& get_slope_z_range() const { return m_slope.z_range; }
|
||||
void set_slope_z_range(const std::array<float, 2>& range) { m_slope.z_range = range; }
|
||||
void set_default_slope_z_range() { m_slope.z_range = { -::cos(Geometry::deg2rad(90.0f - 45.0f)), -::cos(Geometry::deg2rad(90.0f - 70.0f)) }; }
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
|
|
@ -639,10 +668,17 @@ protected:
|
|||
bool on_init_from_file(const std::string& filename) override;
|
||||
};
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
struct _3DScene
|
||||
#else
|
||||
class _3DScene
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
{
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
static GUI::GLCanvas3DManager s_canvas_mgr;
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
public:
|
||||
static std::string get_gl_info(bool format_as_html, bool extensions);
|
||||
|
||||
|
|
@ -654,6 +690,7 @@ public:
|
|||
static void destroy();
|
||||
|
||||
static GUI::GLCanvas3D* get_canvas(wxGLCanvas* canvas);
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
|
||||
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ void AboutDialogLogo::onRepaint(wxEvent &event)
|
|||
// CopyrightsDialog
|
||||
// -----------------------------------------
|
||||
CopyrightsDialog::CopyrightsDialog()
|
||||
: DPIDialog(NULL, wxID_ANY, wxString::FromUTF8((boost::format("%1% - %2%")
|
||||
: 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)
|
||||
|
|
@ -199,7 +199,7 @@ void CopyrightsDialog::onCloseDialog(wxEvent &)
|
|||
}
|
||||
|
||||
AboutDialog::AboutDialog()
|
||||
: DPIDialog(NULL, wxID_ANY, wxString::FromUTF8((boost::format(_utf8(L("About %s"))) % SLIC3R_APP_NAME).str()), 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)
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
|
|
@ -262,12 +262,12 @@ AboutDialog::AboutDialog()
|
|||
const std::string license_str = _utf8(L("GNU Affero General Public License, version 3"));
|
||||
const std::string based_on_str = _utf8(L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community."));
|
||||
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::FromUTF8(
|
||||
const auto text = from_u8(
|
||||
(boost::format(
|
||||
"<html>"
|
||||
"<body bgcolor= %1% link= %2%>"
|
||||
"<font color=%3%>"
|
||||
"%4% © 2016-2019 Prusa Research. <br />"
|
||||
"%4% © 2016-2020 Prusa Research. <br />"
|
||||
"%5% © 2011-2018 Alessandro Ranellucci. <br />"
|
||||
"<a href=\"http://slic3r.org/\">Slic3r</a> %6% "
|
||||
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%7%</a>."
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ void AppConfig::set_recent_projects(const std::vector<std::string>& recent_proje
|
|||
}
|
||||
}
|
||||
|
||||
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed)
|
||||
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed, bool swap_yz)
|
||||
{
|
||||
std::string key = std::string("mouse_device:") + name;
|
||||
auto it = m_storage.find(key);
|
||||
|
|
@ -289,81 +289,18 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe
|
|||
it->second["rotation_speed"] = std::to_string(rotation_speed);
|
||||
it->second["rotation_deadzone"] = std::to_string(rotation_deadzone);
|
||||
it->second["zoom_speed"] = std::to_string(zoom_speed);
|
||||
it->second["swap_yz"] = swap_yz ? "1" : "0";
|
||||
}
|
||||
|
||||
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;
|
||||
auto it = m_storage.find(key);
|
||||
if (it == m_storage.end())
|
||||
return false;
|
||||
|
||||
auto it_val = it->second.find("translation_speed");
|
||||
if (it_val == it->second.end())
|
||||
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;
|
||||
static constexpr char *prefix = "mouse_device:";
|
||||
static constexpr size_t prefix_len = 13; // strlen(prefix); reports error C2131: expression did not evaluate to a constant on VS2019
|
||||
std::vector<std::string> out;
|
||||
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)
|
||||
out.emplace_back(key_value_pair.first.substr(prefix_len));
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppConfig::update_config_dir(const std::string &dir)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string/trim_all.hpp>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Semver.hpp"
|
||||
|
||||
|
|
@ -52,7 +54,13 @@ public:
|
|||
std::string get(const std::string &key) const
|
||||
{ std::string value; this->get("", key, value); return value; }
|
||||
void set(const std::string §ion, const std::string &key, const std::string &value)
|
||||
{
|
||||
{
|
||||
#ifndef _NDEBUG
|
||||
std::string key_trimmed = key;
|
||||
boost::trim_all(key_trimmed);
|
||||
assert(key_trimmed == key);
|
||||
assert(! key_trimmed.empty());
|
||||
#endif // _NDEBUG
|
||||
std::string &old = m_storage[section][key];
|
||||
if (old != value) {
|
||||
old = value;
|
||||
|
|
@ -133,17 +141,39 @@ public:
|
|||
std::vector<std::string> get_recent_projects() const;
|
||||
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);
|
||||
bool get_mouse_device_translation_speed(const std::string& name, double& speed);
|
||||
bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone);
|
||||
bool get_mouse_device_rotation_speed(const std::string& name, float& speed);
|
||||
bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone);
|
||||
bool get_mouse_device_zoom_speed(const std::string& name, double& 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 swap_yz);
|
||||
std::vector<std::string> get_mouse_device_names() const;
|
||||
bool get_mouse_device_translation_speed(const std::string& name, double& speed) const
|
||||
{ return get_3dmouse_device_numeric_value(name, "translation_speed", speed); }
|
||||
bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone) const
|
||||
{ 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); }
|
||||
bool get_mouse_device_swap_yz(const std::string& name, bool& swap) const
|
||||
{ return get_3dmouse_device_numeric_value(name, "swap_yz", swap); }
|
||||
|
||||
static const std::string SECTION_FILAMENTS;
|
||||
static const std::string SECTION_MATERIALS;
|
||||
|
||||
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
|
||||
std::map<std::string, std::map<std::string, std::string>> m_storage;
|
||||
// Map of enabled vendors / models / variants
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "BackgroundSlicingProcess.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/panel.h>
|
||||
|
|
@ -10,9 +11,7 @@
|
|||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
#include <miniz.h>
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
|
||||
#include "libslic3r/Print.hpp"
|
||||
|
|
@ -88,21 +87,15 @@ void BackgroundSlicingProcess::process_fff()
|
|||
assert(m_print == m_fff_print);
|
||||
m_print->process();
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id));
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
|
||||
#else
|
||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||
if (! m_export_path.empty()) {
|
||||
//FIXME localize the messages
|
||||
// Perform the final post-processing of the export path by applying the print statistics over the file name.
|
||||
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
|
||||
GUI::RemovableDriveManager::get_instance().update();
|
||||
bool with_check = GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path);
|
||||
int copy_ret_val = copy_file(m_temp_output_path, export_path, with_check);
|
||||
switch (copy_ret_val){
|
||||
int copy_ret_val = copy_file(m_temp_output_path, export_path, m_export_path_on_removable_media);
|
||||
switch (copy_ret_val) {
|
||||
case SUCCESS: break; // no error
|
||||
case FAIL_COPY_FILE:
|
||||
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
|
||||
|
|
@ -136,7 +129,6 @@ void BackgroundSlicingProcess::process_fff()
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
static void write_thumbnail(Zipper& zipper, const ThumbnailData& data)
|
||||
{
|
||||
size_t png_size = 0;
|
||||
|
|
@ -147,7 +139,6 @@ static void write_thumbnail(Zipper& zipper, const ThumbnailData& data)
|
|||
mz_free(png_data);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
void BackgroundSlicingProcess::process_sla()
|
||||
{
|
||||
|
|
@ -160,7 +151,6 @@ void BackgroundSlicingProcess::process_sla()
|
|||
Zipper zipper(export_path);
|
||||
m_sla_print->export_raster(zipper);
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
if (m_thumbnail_cb != nullptr)
|
||||
{
|
||||
ThumbnailsList thumbnails;
|
||||
|
|
@ -172,7 +162,6 @@ void BackgroundSlicingProcess::process_sla()
|
|||
write_thumbnail(zipper, data);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
zipper.finalize();
|
||||
|
||||
|
|
@ -218,7 +207,7 @@ void BackgroundSlicingProcess::thread_proc()
|
|||
// Canceled, this is all right.
|
||||
assert(m_print->canceled());
|
||||
} catch (const std::bad_alloc& ex) {
|
||||
wxString errmsg = wxString::FromUTF8((boost::format(_utf8(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 "
|
||||
"be glad if you reported it."))) % SLIC3R_APP_NAME).str());
|
||||
error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what());
|
||||
|
|
@ -234,7 +223,7 @@ void BackgroundSlicingProcess::thread_proc()
|
|||
// Only post the canceled event, if canceled by user.
|
||||
// Don't post the canceled event, if canceled from Print::apply().
|
||||
wxCommandEvent evt(m_event_finished_id);
|
||||
evt.SetString(error);
|
||||
evt.SetString(GUI::from_u8(error));
|
||||
evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0));
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||
}
|
||||
|
|
@ -401,7 +390,7 @@ void BackgroundSlicingProcess::set_task(const PrintBase::TaskParams ¶ms)
|
|||
}
|
||||
|
||||
// Set the output path of the G-code.
|
||||
void BackgroundSlicingProcess::schedule_export(const std::string &path)
|
||||
void BackgroundSlicingProcess::schedule_export(const std::string &path, bool export_path_on_removable_media)
|
||||
{
|
||||
assert(m_export_path.empty());
|
||||
if (! m_export_path.empty())
|
||||
|
|
@ -411,6 +400,7 @@ void BackgroundSlicingProcess::schedule_export(const std::string &path)
|
|||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
m_export_path = path;
|
||||
m_export_path_on_removable_media = export_path_on_removable_media;
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job)
|
||||
|
|
@ -431,6 +421,7 @@ void BackgroundSlicingProcess::reset_export()
|
|||
assert(! this->running());
|
||||
if (! this->running()) {
|
||||
m_export_path.clear();
|
||||
m_export_path_on_removable_media = false;
|
||||
// invalidate_step expects the mutex to be locked.
|
||||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
|
|
@ -485,7 +476,6 @@ void BackgroundSlicingProcess::prepare_upload()
|
|||
|
||||
Zipper zipper{source_path.string()};
|
||||
m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string());
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
if (m_thumbnail_cb != nullptr)
|
||||
{
|
||||
ThumbnailsList thumbnails;
|
||||
|
|
@ -497,7 +487,6 @@ void BackgroundSlicingProcess::prepare_upload()
|
|||
write_thumbnail(zipper, data);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
zipper.finalize();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,9 +49,7 @@ public:
|
|||
void set_fff_print(Print *print) { m_fff_print = print; }
|
||||
void set_sla_print(SLAPrint *print) { m_sla_print = print; }
|
||||
void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; }
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
|
||||
// and the background processing will transition into G-code export.
|
||||
|
|
@ -98,7 +96,7 @@ public:
|
|||
|
||||
// Set the export path of the G-code.
|
||||
// Once the path is set, the G-code
|
||||
void schedule_export(const std::string &path);
|
||||
void schedule_export(const std::string &path, bool export_path_on_removable_media);
|
||||
// Set print host upload job data to be enqueued to the PrintHostJobQueue
|
||||
// after current print slicing is complete
|
||||
void schedule_upload(Slic3r::PrintHostJob upload_job);
|
||||
|
|
@ -155,15 +153,14 @@ private:
|
|||
SLAPrint *m_sla_print = nullptr;
|
||||
// Data structure, to which the G-code export writes its annotations.
|
||||
GCodePreviewData *m_gcode_preview_data = nullptr;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
// Callback function, used to write thumbnails into gcode.
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
|
||||
std::string m_temp_output_path;
|
||||
// Output path provided by the user. The output path may be set even if the slicing is running,
|
||||
// but once set, it cannot be re-set.
|
||||
std::string m_export_path;
|
||||
bool m_export_path_on_removable_media = false;
|
||||
// Print host upload job to schedule after slicing is complete, used by schedule_upload(),
|
||||
// empty by default (ie. no upload to schedule)
|
||||
PrintHostJob m_upload_job;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
|
|||
ConfigOptionDef def;
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
|
||||
def.min = 0;
|
||||
def.max = 1200;
|
||||
def.label = L("Size");
|
||||
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||
Option option(def, "rect_size");
|
||||
|
|
@ -81,6 +83,8 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
|
|||
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
|
||||
def.min = -600;
|
||||
def.max = 600;
|
||||
def.label = L("Origin");
|
||||
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
option = Option(def, "rect_origin");
|
||||
|
|
|
|||
|
|
@ -219,10 +219,10 @@ void BonjourDialog::on_timer(wxTimerEvent &)
|
|||
|
||||
if (timer_state > 0) {
|
||||
const std::string dots(timer_state, '.');
|
||||
label->SetLabel(wxString::FromUTF8((boost::format("%1% %2%") % search_str % dots).str()));
|
||||
label->SetLabel(GUI::from_u8((boost::format("%1% %2%") % search_str % dots).str()));
|
||||
timer_state = (timer_state) % 3 + 1;
|
||||
} else {
|
||||
label->SetLabel(wxString::FromUTF8((boost::format("%1%: %2%") % search_str % (_utf8(L("Finished"))+".")).str()));
|
||||
label->SetLabel(GUI::from_u8((boost::format("%1%: %2%") % search_str % (_utf8(L("Finished"))+".")).str()));
|
||||
timer->Stop();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "Camera.hpp"
|
||||
#if !ENABLE_THUMBNAIL_GENERATOR
|
||||
#include "3DScene.hpp"
|
||||
#endif // !ENABLE_THUMBNAIL_GENERATOR
|
||||
#include "GUI_App.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
|
|
@ -25,10 +22,8 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
const double Camera::DefaultDistance = 1000.0;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
const double Camera::DefaultZoomToBoxMarginFactor = 1.025;
|
||||
const double Camera::DefaultZoomToVolumesMarginFactor = 1.025;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
double Camera::FrustrumMinZRange = 50.0;
|
||||
double Camera::FrustrumMinNearZ = 100.0;
|
||||
double Camera::FrustrumZMargin = 10.0;
|
||||
|
|
@ -219,18 +214,10 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa
|
|||
glsafe(::glMatrixMode(GL_MODELVIEW));
|
||||
}
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor)
|
||||
#else
|
||||
void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h)
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
{
|
||||
// Calculate the zoom factor needed to adjust the view around the given box.
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor);
|
||||
#else
|
||||
double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h);
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
if (zoom > 0.0)
|
||||
{
|
||||
m_zoom = zoom;
|
||||
|
|
@ -239,7 +226,6 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h)
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor)
|
||||
{
|
||||
Vec3d center;
|
||||
|
|
@ -251,7 +237,6 @@ void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor)
|
|||
set_target(center);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
void Camera::debug_render() const
|
||||
|
|
@ -260,7 +245,7 @@ void Camera::debug_render() const
|
|||
imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
std::string type = get_type_as_string();
|
||||
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"))
|
||||
type += "/free";
|
||||
else
|
||||
type += "/constrained";
|
||||
|
|
@ -387,11 +372,7 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor) const
|
||||
#else
|
||||
double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
{
|
||||
double max_bb_size = box.max_size();
|
||||
if (max_bb_size == 0.0)
|
||||
|
|
@ -423,11 +404,6 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca
|
|||
double max_x = -DBL_MAX;
|
||||
double max_y = -DBL_MAX;
|
||||
|
||||
#if !ENABLE_THUMBNAIL_GENERATOR
|
||||
// margin factor to give some empty space around the box
|
||||
double margin_factor = 1.25;
|
||||
#endif // !ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
for (const Vec3d& v : vertices)
|
||||
{
|
||||
// project vertex on the plane perpendicular to camera forward axis
|
||||
|
|
@ -458,7 +434,6 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca
|
|||
return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy);
|
||||
}
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& center, double margin_factor) const
|
||||
{
|
||||
if (volumes.empty())
|
||||
|
|
@ -519,7 +494,6 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c
|
|||
|
||||
return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy);
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
void Camera::set_distance(double distance) const
|
||||
{
|
||||
|
|
@ -537,6 +511,7 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up
|
|||
Vec3d unit_y = unit_z.cross(unit_x).normalized();
|
||||
|
||||
m_target = target;
|
||||
m_distance = (position - target).norm();
|
||||
Vec3d new_position = m_target + m_distance * unit_z;
|
||||
|
||||
m_view_matrix(0, 0) = unit_x(0);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
#define slic3r_Camera_hpp_
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
#include "3DScene.hpp"
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
#include <array>
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -13,10 +11,8 @@ namespace GUI {
|
|||
struct Camera
|
||||
{
|
||||
static const double DefaultDistance;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
static const double DefaultZoomToBoxMarginFactor;
|
||||
static const double DefaultZoomToVolumesMarginFactor;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
static double FrustrumMinZRange;
|
||||
static double FrustrumMinNearZ;
|
||||
static double FrustrumZMargin;
|
||||
|
|
@ -97,12 +93,8 @@ public:
|
|||
// If larger z span is needed, pass the desired values of near and far z (negative values are ignored)
|
||||
void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0) const;
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
void zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor);
|
||||
void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor);
|
||||
#else
|
||||
void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h);
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
void debug_render() const;
|
||||
|
|
@ -122,6 +114,15 @@ public:
|
|||
// returns true if the camera z axis (forward) is pointing in the negative direction of the world z axis
|
||||
bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; }
|
||||
|
||||
// forces camera right vector to be parallel to XY plane
|
||||
void recover_from_free_camera()
|
||||
{
|
||||
if (std::abs(get_dir_right()(2)) > EPSILON)
|
||||
look_at(get_position(), m_target, Vec3d::UnitZ());
|
||||
}
|
||||
|
||||
void look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up);
|
||||
|
||||
double max_zoom() const { return 100.0; }
|
||||
double min_zoom() const;
|
||||
|
||||
|
|
@ -129,15 +130,10 @@ private:
|
|||
// returns tight values for nearZ and farZ plane around the given bounding box
|
||||
// the camera MUST be outside of the bounding box in eye coordinate of the given box
|
||||
std::pair<double, double> calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor) const;
|
||||
double calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& center, double margin_factor = DefaultZoomToVolumesMarginFactor) const;
|
||||
#else
|
||||
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
void set_distance(double distance) const;
|
||||
|
||||
void look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up);
|
||||
void set_default_orientation();
|
||||
Vec3d validate_target(const Vec3d& target) const;
|
||||
void update_zenit();
|
||||
|
|
|
|||
|
|
@ -268,8 +268,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||
"bridge_acceleration", "first_layer_acceleration" })
|
||||
toggle_field(el, have_default_acceleration);
|
||||
|
||||
bool have_skirt = config->opt_int("skirts") > 0 || config->opt_float("min_skirt_length") > 0;
|
||||
for (auto el : { "skirt_distance", "skirt_height" })
|
||||
bool have_skirt = config->opt_int("skirts") > 0;
|
||||
toggle_field("skirt_height", have_skirt && !config->opt_bool("draft_shield"));
|
||||
for (auto el : { "skirt_distance", "draft_shield", "min_skirt_length" })
|
||||
toggle_field(el, have_skirt);
|
||||
|
||||
bool have_brim = config->opt_float("brim_width") > 0;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
|
|||
}
|
||||
|
||||
if (! compatible) {
|
||||
text += "<p align=\"right\">" + wxString::FromUTF8((boost::format(_utf8(L("Incompatible with this %s"))) % SLIC3R_APP_NAME).str()) + "</p>";
|
||||
text += "<p align=\"right\">" + from_u8((boost::format(_utf8(L("Incompatible with this %s"))) % SLIC3R_APP_NAME).str()) + "</p>";
|
||||
}
|
||||
else if (! snapshot_active)
|
||||
text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">" + _(L("Activate")) + "</a></p>";
|
||||
|
|
|
|||
|
|
@ -42,16 +42,27 @@ using Config::SnapshotDB;
|
|||
|
||||
// Configuration data structures extensions needed for the wizard
|
||||
|
||||
Bundle::Bundle(fs::path source_path, bool is_in_resources, bool is_prusa_bundle)
|
||||
: preset_bundle(new PresetBundle)
|
||||
, vendor_profile(nullptr)
|
||||
, is_in_resources(is_in_resources)
|
||||
, is_prusa_bundle(is_prusa_bundle)
|
||||
bool Bundle::load(fs::path source_path, bool ais_in_resources, bool ais_prusa_bundle)
|
||||
{
|
||||
preset_bundle->load_configbundle(source_path.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
||||
this->preset_bundle = std::make_unique<PresetBundle>();
|
||||
this->is_in_resources = ais_in_resources;
|
||||
this->is_prusa_bundle = ais_prusa_bundle;
|
||||
|
||||
std::string path_string = source_path.string();
|
||||
size_t presets_loaded = preset_bundle->load_configbundle(path_string, PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
||||
auto first_vendor = preset_bundle->vendors.begin();
|
||||
wxCHECK_RET(first_vendor != preset_bundle->vendors.end(), "Failed to load preset bundle");
|
||||
vendor_profile = &first_vendor->second;
|
||||
if (first_vendor == preset_bundle->vendors.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No vendor information defined, cannot install.") % path_string;
|
||||
return false;
|
||||
}
|
||||
if (presets_loaded == 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No profile loaded.") % path_string;
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << boost::format("Vendor bundle: `%1%`: %2% profiles loaded.") % path_string % presets_loaded;
|
||||
this->vendor_profile = &first_vendor->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bundle::Bundle(Bundle &&other)
|
||||
|
|
@ -76,8 +87,11 @@ BundleMap BundleMap::load()
|
|||
prusa_bundle_path = (rsrc_vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini");
|
||||
prusa_bundle_rsrc = true;
|
||||
}
|
||||
Bundle prusa_bundle(std::move(prusa_bundle_path), prusa_bundle_rsrc, true);
|
||||
res.emplace(PresetBundle::PRUSA_BUNDLE, std::move(prusa_bundle));
|
||||
{
|
||||
Bundle prusa_bundle;
|
||||
if (prusa_bundle.load(std::move(prusa_bundle_path), prusa_bundle_rsrc, true))
|
||||
res.emplace(PresetBundle::PRUSA_BUNDLE, std::move(prusa_bundle));
|
||||
}
|
||||
|
||||
// Load the other bundles in the datadir/vendor directory
|
||||
// and then additionally from resources/profiles.
|
||||
|
|
@ -90,8 +104,9 @@ BundleMap BundleMap::load()
|
|||
// Don't load this bundle if we've already loaded it.
|
||||
if (res.find(id) != res.end()) { continue; }
|
||||
|
||||
Bundle bundle(dir_entry.path(), is_in_resources);
|
||||
res.emplace(std::move(id), std::move(bundle));
|
||||
Bundle bundle;
|
||||
if (bundle.load(dir_entry.path(), is_in_resources))
|
||||
res.emplace(std::move(id), std::move(bundle));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +188,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
|
|||
|
||||
wxBitmap bitmap;
|
||||
int bitmap_width = 0;
|
||||
const wxString bitmap_file = GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str()));
|
||||
const wxString bitmap_file = GUI::from_u8(Slic3r::resources_dir() + "/profiles/" + vendor.id + "/" + model.id + "_thumbnail.png");
|
||||
if (wxFileExists(bitmap_file)) {
|
||||
bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG);
|
||||
bitmap_width = bitmap.GetWidth();
|
||||
|
|
@ -422,20 +437,20 @@ void ConfigWizardPage::append_spacer(int space)
|
|||
// Wizard pages
|
||||
|
||||
PageWelcome::PageWelcome(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, wxString::FromUTF8((boost::format(
|
||||
: ConfigWizardPage(parent, from_u8((boost::format(
|
||||
#ifdef __APPLE__
|
||||
_utf8(L("Welcome to the %s Configuration Assistant"))
|
||||
#else
|
||||
_utf8(L("Welcome to the %s Configuration Wizard"))
|
||||
#endif
|
||||
) % SLIC3R_APP_NAME).str()), _(L("Welcome")))
|
||||
, welcome_text(append_text(wxString::FromUTF8((boost::format(
|
||||
, welcome_text(append_text(from_u8((boost::format(
|
||||
_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
|
||||
% _utf8(ConfigWizard::name())).str())
|
||||
))
|
||||
, 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 (a snapshot will be taken beforehand)")))
|
||||
))
|
||||
{
|
||||
welcome_text->Hide();
|
||||
|
|
@ -478,7 +493,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent,
|
|||
continue;
|
||||
}
|
||||
|
||||
const auto picker_title = family.empty() ? wxString() : wxString::FromUTF8((boost::format(_utf8(L("%s Family"))) % family).str());
|
||||
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);
|
||||
|
||||
picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) {
|
||||
|
|
@ -1458,12 +1473,41 @@ void ConfigWizard::priv::load_vendors()
|
|||
pair.second.preset_bundle->load_installed_printers(appconfig_new);
|
||||
}
|
||||
|
||||
if (app_config->has_section(AppConfig::SECTION_FILAMENTS)) {
|
||||
appconfig_new.set_section(AppConfig::SECTION_FILAMENTS, app_config->get_section(AppConfig::SECTION_FILAMENTS));
|
||||
}
|
||||
if (app_config->has_section(AppConfig::SECTION_MATERIALS)) {
|
||||
appconfig_new.set_section(AppConfig::SECTION_MATERIALS, app_config->get_section(AppConfig::SECTION_MATERIALS));
|
||||
}
|
||||
// Copy installed filaments and SLA material names from app_config to appconfig_new
|
||||
// while resolving current names of profiles, which were renamed in the meantime.
|
||||
for (PrinterTechnology technology : { ptFFF, ptSLA }) {
|
||||
const std::string §ion_name = (technology == ptFFF) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS;
|
||||
std::map<std::string, std::string> section_new;
|
||||
if (app_config->has_section(section_name)) {
|
||||
const std::map<std::string, std::string> §ion_old = app_config->get_section(section_name);
|
||||
for (const std::pair<std::string, std::string> &material_name_and_installed : section_old)
|
||||
if (material_name_and_installed.second == "1") {
|
||||
// Material is installed. Resolve it in bundles.
|
||||
size_t num_found = 0;
|
||||
const std::string &material_name = material_name_and_installed.first;
|
||||
for (auto &bundle : bundles) {
|
||||
const PresetCollection &materials = bundle.second.preset_bundle->materials(technology);
|
||||
const Preset *preset = materials.find_preset(material_name);
|
||||
if (preset == nullptr) {
|
||||
// Not found. Maybe the material preset is there, bu it was was renamed?
|
||||
const std::string *new_name = materials.get_preset_name_renamed(material_name);
|
||||
if (new_name != nullptr)
|
||||
preset = materials.find_preset(*new_name);
|
||||
}
|
||||
if (preset != nullptr) {
|
||||
// Materal preset was found, mark it as installed.
|
||||
section_new[preset->name] = "1";
|
||||
++ num_found;
|
||||
}
|
||||
}
|
||||
if (num_found == 0)
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Profile %1% was not found in installed vendor Preset Bundles.") % material_name;
|
||||
else if (num_found > 1)
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Profile %1% was found in %2% vendor Preset Bundles.") % material_name % num_found;
|
||||
}
|
||||
}
|
||||
appconfig_new.set_section(section_name, section_new);
|
||||
};
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::add_page(ConfigWizardPage *page)
|
||||
|
|
@ -1627,9 +1671,9 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker
|
|||
}
|
||||
}
|
||||
|
||||
// if at list one printer is selected but there in no one selected material,
|
||||
// select materials which is default for selected printer(s)
|
||||
select_default_materials_if_needed(pair.second.vendor_profile, page->technology, evt.model_id);
|
||||
// When a printer model is picked, but there is no material installed compatible with this printer model,
|
||||
// install default materials for selected printer model silently.
|
||||
check_and_install_missing_materials(page->technology, evt.model_id);
|
||||
}
|
||||
|
||||
if (page->technology & T_FFF) {
|
||||
|
|
@ -1639,41 +1683,26 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker
|
|||
}
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::select_default_materials_for_printer_model(const std::vector<VendorProfile::PrinterModel>& models, Technology technology, const std::string& model_id)
|
||||
void ConfigWizard::priv::select_default_materials_for_printer_model(const VendorProfile::PrinterModel &printer_model, Technology technology)
|
||||
{
|
||||
PageMaterials* page_materials = technology & T_FFF ? page_filaments : page_sla_materials;
|
||||
|
||||
auto it = std::find_if(models.begin(), models.end(), [model_id](VendorProfile::PrinterModel model) {return model_id == model.id; });
|
||||
if (it != models.end())
|
||||
for (const std::string& material : it->default_materials)
|
||||
appconfig_new.set(page_materials->materials->appconfig_section(), material, "1");
|
||||
for (const std::string& material : printer_model.default_materials)
|
||||
appconfig_new.set(page_materials->materials->appconfig_section(), material, "1");
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::select_default_materials_if_needed(VendorProfile* vendor_profile, Technology technology, const std::string& model_id)
|
||||
void ConfigWizard::priv::select_default_materials_for_printer_models(Technology technology, const std::set<const VendorProfile::PrinterModel*> &printer_models)
|
||||
{
|
||||
if ((technology & T_FFF && !any_fff_selected) ||
|
||||
(technology & T_SLA && !any_sla_selected) ||
|
||||
check_materials_in_config(technology, false))
|
||||
return;
|
||||
PageMaterials *page_materials = technology & T_FFF ? page_filaments : page_sla_materials;
|
||||
const std::string &appconfig_section = page_materials->materials->appconfig_section();
|
||||
|
||||
select_default_materials_for_printer_model(vendor_profile->models, technology, model_id);
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::selected_default_materials(Technology technology)
|
||||
{
|
||||
auto select_default_materials_for_printer_page = [this](PagePrinters * page_printers, Technology technology)
|
||||
auto select_default_materials_for_printer_page = [this, appconfig_section, printer_models](PagePrinters *page_printers, Technology technology)
|
||||
{
|
||||
std::set<std::string> selected_models = page_printers->get_selected_models();
|
||||
const std::string vendor_id = page_printers->get_vendor_id();
|
||||
|
||||
const std::string vendor_id = page_printers->get_vendor_id();
|
||||
for (auto& pair : bundles)
|
||||
{
|
||||
if (pair.first != vendor_id)
|
||||
continue;
|
||||
|
||||
for (const std::string& model_id : selected_models)
|
||||
select_default_materials_for_printer_model(pair.second.vendor_profile->models, technology, model_id);
|
||||
}
|
||||
if (pair.first == vendor_id)
|
||||
for (const VendorProfile::PrinterModel *printer_model : printer_models)
|
||||
for (const std::string &material : printer_model->default_materials)
|
||||
appconfig_new.set(appconfig_section, material, "1");
|
||||
};
|
||||
|
||||
PagePrinters* page_printers = technology & T_FFF ? page_fff : page_msla;
|
||||
|
|
@ -1687,7 +1716,7 @@ void ConfigWizard::priv::selected_default_materials(Technology technology)
|
|||
}
|
||||
|
||||
update_materials(technology);
|
||||
(technology& T_FFF ? page_filaments : page_sla_materials)->reload_presets();
|
||||
((technology & T_FFF) ? page_filaments : page_sla_materials)->reload_presets();
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install)
|
||||
|
|
@ -1728,51 +1757,105 @@ bool ConfigWizard::priv::on_bnt_finish()
|
|||
// theres no need to check that filament is selected if we have only custom printer
|
||||
if (custom_printer_selected && !any_fff_selected && !any_sla_selected) return true;
|
||||
// check, that there is selected at least one filament/material
|
||||
return check_materials_in_config(T_ANY);
|
||||
return check_and_install_missing_materials(T_ANY);
|
||||
}
|
||||
|
||||
bool ConfigWizard::priv::check_materials_in_config(Technology technology, bool show_info_msg)
|
||||
// This allmighty method verifies, whether there is at least a single compatible filament or SLA material installed
|
||||
// for each Printer preset of each Printer Model installed.
|
||||
//
|
||||
// In case only_for_model_id is set, then the test is done for that particular printer model only, and the default materials are installed silently.
|
||||
// Otherwise the user is quieried whether to install the missing default materials or not.
|
||||
//
|
||||
// Return true if the tested Printer Models already had materials installed.
|
||||
// Return false if there were some Printer Models with missing materials, independent from whether the defaults were installed for these
|
||||
// respective Printer Models or not.
|
||||
bool ConfigWizard::priv::check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id)
|
||||
{
|
||||
const auto exist_preset = [this](const std::string& section, const Materials& materials)
|
||||
// Walk over all installed Printer presets and verify whether there is a filament or SLA material profile installed at the same PresetBundle,
|
||||
// which is compatible with it.
|
||||
const auto printer_models_missing_materials = [this, only_for_model_id](PrinterTechnology technology, const std::string §ion)
|
||||
{
|
||||
if (appconfig_new.has_section(section) &&
|
||||
!appconfig_new.get_section(section).empty())
|
||||
{
|
||||
const std::map<std::string, std::string>& appconfig_presets = appconfig_new.get_section(section);
|
||||
for (const auto& preset : appconfig_presets)
|
||||
if (materials.exist_preset(preset.first))
|
||||
return true;
|
||||
const std::map<std::string, std::string> &appconfig_presets = appconfig_new.has_section(section) ? appconfig_new.get_section(section) : std::map<std::string, std::string>();
|
||||
std::set<const VendorProfile::PrinterModel*> printer_models_without_material;
|
||||
for (const auto &pair : bundles) {
|
||||
const PresetCollection &materials = pair.second.preset_bundle->materials(technology);
|
||||
for (const auto &printer : pair.second.preset_bundle->printers) {
|
||||
if (printer.is_visible && printer.printer_technology() == technology) {
|
||||
const VendorProfile::PrinterModel *printer_model = PresetUtils::system_printer_model(printer);
|
||||
assert(printer_model != nullptr);
|
||||
if ((only_for_model_id.empty() || only_for_model_id == printer_model->id) &&
|
||||
printer_models_without_material.find(printer_model) == printer_models_without_material.end()) {
|
||||
bool has_material = false;
|
||||
for (const std::pair<std::string, std::string> &preset : appconfig_presets) {
|
||||
if (preset.second == "1") {
|
||||
const Preset *material = materials.find_preset(preset.first, false);
|
||||
if (material != nullptr && is_compatible_with_printer(PresetWithVendorProfile(*material, nullptr), PresetWithVendorProfile(printer, nullptr))) {
|
||||
has_material = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! has_material)
|
||||
printer_models_without_material.insert(printer_model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
assert(printer_models_without_material.empty() || only_for_model_id.empty() || only_for_model_id == (*printer_models_without_material.begin())->id);
|
||||
return printer_models_without_material;
|
||||
};
|
||||
|
||||
const auto ask_and_selected_default_materials = [this](wxString message, Technology technology)
|
||||
const auto ask_and_select_default_materials = [this](const wxString &message, const std::set<const VendorProfile::PrinterModel*> &printer_models, Technology technology)
|
||||
{
|
||||
wxMessageDialog msg(q, message, _(L("Notice")), wxYES_NO);
|
||||
if (msg.ShowModal() == wxID_YES)
|
||||
selected_default_materials(technology);
|
||||
select_default_materials_for_printer_models(technology, printer_models);
|
||||
};
|
||||
|
||||
if (any_fff_selected && technology & T_FFF && !exist_preset(AppConfig::SECTION_FILAMENTS, filaments))
|
||||
{
|
||||
if (show_info_msg)
|
||||
{
|
||||
wxString message = _(L("You have to select at least one filament for selected printers")) + "\n\n\t" +
|
||||
_(L("Do you want to automatic select default filaments?"));
|
||||
ask_and_selected_default_materials(message, T_FFF);
|
||||
const auto printer_model_list = [](const std::set<const VendorProfile::PrinterModel*> &printer_models) -> wxString {
|
||||
wxString out;
|
||||
for (const VendorProfile::PrinterModel *printer_model : printer_models) {
|
||||
out += "\t\t";
|
||||
out += from_u8(printer_model->name);
|
||||
out += "\n";
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
if (any_fff_selected && (technology & T_FFF)) {
|
||||
std::set<const VendorProfile::PrinterModel*> printer_models_without_material = printer_models_missing_materials(ptFFF, AppConfig::SECTION_FILAMENTS);
|
||||
if (! printer_models_without_material.empty()) {
|
||||
if (only_for_model_id.empty())
|
||||
ask_and_select_default_materials(
|
||||
_L("The following FFF printer models have no filament selected:") +
|
||||
"\n\n\t" +
|
||||
printer_model_list(printer_models_without_material) +
|
||||
"\n\n\t" +
|
||||
_L("Do you want to select default filaments for these FFF printer models?"),
|
||||
printer_models_without_material,
|
||||
T_FFF);
|
||||
else
|
||||
select_default_materials_for_printer_model(**printer_models_without_material.begin(), T_FFF);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (any_sla_selected && technology & T_SLA && !exist_preset(AppConfig::SECTION_MATERIALS, sla_materials))
|
||||
{
|
||||
if (show_info_msg)
|
||||
{
|
||||
wxString message = _(L("You have to select at least one material for selected printers")) + "\n\n\t" +
|
||||
_(L("Do you want to automatic select default materials?"));
|
||||
ask_and_selected_default_materials(message, T_SLA);
|
||||
}
|
||||
return false;
|
||||
if (any_sla_selected && (technology & T_SLA)) {
|
||||
std::set<const VendorProfile::PrinterModel*> printer_models_without_material = printer_models_missing_materials(ptSLA, AppConfig::SECTION_MATERIALS);
|
||||
if (! printer_models_without_material.empty()) {
|
||||
if (only_for_model_id.empty())
|
||||
ask_and_select_default_materials(
|
||||
_L("The following SLA printer models have no materials selected:") +
|
||||
"\n\n\t" +
|
||||
printer_model_list(printer_models_without_material) +
|
||||
"\n\n\t" +
|
||||
_L("Do you want to select default SLA materials for these printer models?"),
|
||||
printer_models_without_material,
|
||||
T_SLA);
|
||||
else
|
||||
select_default_materials_for_printer_model(**printer_models_without_material.begin(), T_SLA);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -1911,6 +1994,7 @@ void ConfigWizard::priv::update_presets_in_config(const std::string& section, co
|
|||
const PresetAliases& aliases = section == AppConfig::SECTION_FILAMENTS ? aliases_fff : aliases_sla;
|
||||
|
||||
auto update = [this, add](const std::string& s, const std::string& key) {
|
||||
assert(! s.empty());
|
||||
if (add)
|
||||
appconfig_new.set(s, key, "1");
|
||||
else
|
||||
|
|
@ -2046,8 +2130,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
{
|
||||
// check, that there is selected at least one filament/material
|
||||
ConfigWizardPage* active_page = this->p->index->active_page();
|
||||
if ( (active_page == p->page_filaments || active_page == p->page_sla_materials)
|
||||
&& !p->check_materials_in_config(dynamic_cast<PageMaterials*>(active_page)->materials->technology))
|
||||
if (// Leaving the filaments or SLA materials page and
|
||||
(active_page == p->page_filaments || active_page == p->page_sla_materials) &&
|
||||
// some Printer models had no filament or SLA material selected.
|
||||
! p->check_and_install_missing_materials(dynamic_cast<PageMaterials*>(active_page)->materials->technology))
|
||||
// In that case don't leave the page and the function above queried the user whether to install default materials.
|
||||
return;
|
||||
this->p->index->go_next();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -82,14 +82,6 @@ struct Materials
|
|||
}
|
||||
}
|
||||
|
||||
bool exist_preset(const std::string& preset_name) const
|
||||
{
|
||||
for (const Preset* preset : presets)
|
||||
if (preset->name == preset_name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static const std::string UNKNOWN;
|
||||
static const std::string& get_filament_type(const Preset *preset);
|
||||
static const std::string& get_filament_vendor(const Preset *preset);
|
||||
|
|
@ -100,13 +92,16 @@ struct Materials
|
|||
struct Bundle
|
||||
{
|
||||
std::unique_ptr<PresetBundle> preset_bundle;
|
||||
VendorProfile *vendor_profile;
|
||||
const bool is_in_resources;
|
||||
const bool is_prusa_bundle;
|
||||
VendorProfile *vendor_profile { nullptr };
|
||||
bool is_in_resources { false };
|
||||
bool is_prusa_bundle { false };
|
||||
|
||||
Bundle(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false);
|
||||
Bundle() = default;
|
||||
Bundle(Bundle &&other);
|
||||
|
||||
// Returns false if not loaded. Reason for that is logged as boost::log error.
|
||||
bool load(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false);
|
||||
|
||||
const std::string& vendor_id() const { return vendor_profile->id; }
|
||||
};
|
||||
|
||||
|
|
@ -500,17 +495,12 @@ struct ConfigWizard::priv
|
|||
|
||||
void on_custom_setup(const bool custom_wanted);
|
||||
void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
|
||||
void select_default_materials_for_printer_model(const std::vector<VendorProfile::PrinterModel> &models,
|
||||
Technology technology,
|
||||
const std::string & model_id);
|
||||
void select_default_materials_if_needed(VendorProfile* vendor_profile,
|
||||
Technology technology,
|
||||
const std::string &model_id);
|
||||
void selected_default_materials(Technology technology);
|
||||
void select_default_materials_for_printer_model(const VendorProfile::PrinterModel &printer_model, Technology technology);
|
||||
void select_default_materials_for_printer_models(Technology technology, const std::set<const VendorProfile::PrinterModel*> &printer_models);
|
||||
void on_3rdparty_install(const VendorProfile *vendor, bool install);
|
||||
|
||||
bool on_bnt_finish();
|
||||
bool check_materials_in_config(Technology technology, bool show_info_msg = true);
|
||||
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
|
||||
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
// #ys_FIXME_alise
|
||||
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
||||
|
|
|
|||
|
|
@ -20,11 +20,13 @@
|
|||
#include <cmath>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include "Field.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using GUI::from_u8;
|
||||
using GUI::into_u8;
|
||||
using GUI::format_wxstr;
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
|
|
@ -530,7 +532,7 @@ wxString Control::get_label(int tick) const
|
|||
const wxString str = m_values.empty() ?
|
||||
wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) :
|
||||
wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None);
|
||||
return from_u8((boost::format("%1%\n(%2%)") % str % (m_values.empty() ? value : value+1)).str());
|
||||
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value+1);
|
||||
}
|
||||
|
||||
void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const
|
||||
|
|
@ -952,7 +954,7 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
|||
return _(L("Discard all custom changes"));
|
||||
if (m_focus == fiCogIcon)
|
||||
return m_mode == t_mode::MultiAsSingle ?
|
||||
wxString::FromUTF8((boost::format(_utf8(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").str()) :
|
||||
_(L("Jump to height")) + " (Shift + G)";
|
||||
if (m_focus == fiColorBand)
|
||||
|
|
@ -1001,16 +1003,16 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
|||
|
||||
// Show custom Gcode as a first string of tooltop
|
||||
tooltip = " ";
|
||||
tooltip += tick_code_it->gcode == ColorChangeCode ? ( m_mode == t_mode::SingleExtruder ?
|
||||
from_u8((boost::format(_utf8(L("Color change (\"%1%\")"))) % tick_code_it->gcode ).str()) :
|
||||
from_u8((boost::format(_utf8(L("Color change (\"%1%\") for Extruder %2%"))) %
|
||||
tick_code_it->gcode % tick_code_it->extruder).str()) ) :
|
||||
tick_code_it->gcode == PausePrintCode ?
|
||||
from_u8((boost::format(_utf8(L("Pause print (\"%1%\")"))) % tick_code_it->gcode ).str()) :
|
||||
tick_code_it->gcode == ToolChangeCode ?
|
||||
from_u8((boost::format(_utf8(L("Extruder (tool) is changed to Extruder \"%1%\""))) %
|
||||
tick_code_it->extruder ).str()) :
|
||||
from_u8(tick_code_it->gcode);
|
||||
tooltip +=
|
||||
tick_code_it->gcode == ColorChangeCode ?
|
||||
(m_mode == t_mode::SingleExtruder ?
|
||||
format_wxstr(_L("Color change (\"%1%\")"), tick_code_it->gcode) :
|
||||
format_wxstr(_L("Color change (\"%1%\") for Extruder %2%"), tick_code_it->gcode, tick_code_it->extruder)) :
|
||||
tick_code_it->gcode == PausePrintCode ?
|
||||
format_wxstr(_L("Pause print (\"%1%\")"), tick_code_it->gcode) :
|
||||
tick_code_it->gcode == ToolChangeCode ?
|
||||
format_wxstr(_L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) :
|
||||
from_u8(tick_code_it->gcode);
|
||||
|
||||
// If tick is marked as a conflict (exclamation icon),
|
||||
// we should to explain why
|
||||
|
|
@ -1176,8 +1178,8 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren
|
|||
}
|
||||
|
||||
const wxString menu_name = switch_current_code ?
|
||||
from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % ColorChangeCode).str()) :
|
||||
from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % ColorChangeCode).str());
|
||||
format_wxstr(_L("Switch code to Color change (%1%) for:"), ColorChangeCode) :
|
||||
format_wxstr(_L("Add color change (%1%) for:"), ColorChangeCode);
|
||||
wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, "");
|
||||
add_color_change_menu_item->SetBitmap(create_scaled_bitmap("colorchange_add_m"));
|
||||
}
|
||||
|
|
@ -1615,8 +1617,8 @@ static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0,
|
|||
|
||||
static std::string get_custom_code(const std::string& code_in, double height)
|
||||
{
|
||||
wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + ":";
|
||||
wxString msg_header = from_u8((boost::format(_utf8(L("Custom G-code on current layer (%1% mm)."))) % height).str());
|
||||
wxString msg_text = _L("Enter custom G-code used on current layer") + ":";
|
||||
wxString msg_header = format_wxstr(_L("Custom G-code on current layer (%1% mm)."), height);
|
||||
|
||||
// get custom gcode
|
||||
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in,
|
||||
|
|
@ -1631,8 +1633,8 @@ static std::string get_custom_code(const std::string& code_in, double height)
|
|||
|
||||
static std::string get_pause_print_msg(const std::string& msg_in, double height)
|
||||
{
|
||||
wxString msg_text = from_u8(_utf8(L("Enter short message shown on Printer display when a print is paused"))) + ":";
|
||||
wxString msg_header = from_u8((boost::format(_utf8(L("Message for pause print on current layer (%1% mm)."))) % height).str());
|
||||
wxString msg_text = _L("Enter short message shown on Printer display when a print is paused") + ":";
|
||||
wxString msg_header = format_wxstr(_L("Message for pause print on current layer (%1% mm)."), height);
|
||||
|
||||
// get custom gcode
|
||||
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in),
|
||||
|
|
@ -1914,8 +1916,8 @@ bool Control::check_ticks_changed_event(const std::string& gcode)
|
|||
{
|
||||
wxString message = m_mode == t_mode::SingleExtruder ? (
|
||||
_(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"
|
||||
"NO if you want all tool changes switch to color 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"
|
||||
"or CANCEL to leave it unchanged.")) + "\n\n\t" +
|
||||
_(L("Do you want to delete all saved tool changes?"))
|
||||
) : ( // t_mode::MultiExtruder
|
||||
|
|
|
|||
|
|
@ -40,11 +40,19 @@ template<class T, size_t N> struct ArrayEvent : public wxEvent
|
|||
return new ArrayEvent<T, N>(GetEventType(), data, GetEventObject());
|
||||
}
|
||||
};
|
||||
template<class T> struct ArrayEvent<T, 1> : public wxEvent
|
||||
|
||||
template<class T> struct Event : public wxEvent
|
||||
{
|
||||
T data;
|
||||
|
||||
ArrayEvent(wxEventType type, T data, wxObject* origin = nullptr)
|
||||
Event(wxEventType type, const T &data, wxObject* origin = nullptr)
|
||||
: wxEvent(0, type), data(std::move(data))
|
||||
{
|
||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||
SetEventObject(origin);
|
||||
}
|
||||
|
||||
Event(wxEventType type, T&& data, wxObject* origin = nullptr)
|
||||
: wxEvent(0, type), data(std::move(data))
|
||||
{
|
||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||
|
|
@ -53,13 +61,10 @@ template<class T> struct ArrayEvent<T, 1> : public wxEvent
|
|||
|
||||
virtual wxEvent* Clone() const
|
||||
{
|
||||
return new ArrayEvent<T, 1>(GetEventType(), data, GetEventObject());
|
||||
return new Event<T>(GetEventType(), data, GetEventObject());
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> using Event = ArrayEvent<T, 1>;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -375,12 +375,11 @@ void TextCtrl::BUILD() {
|
|||
{
|
||||
e.Skip();
|
||||
#ifdef __WXOSX__
|
||||
// OSX issue: For some unknown reason wxEVT_KILL_FOCUS is emitted twice in a row
|
||||
// OSX issue: For some unknown reason wxEVT_KILL_FOCUS is emitted twice in a row in some cases
|
||||
// (like when information dialog is shown during an update of the option value)
|
||||
// Thus, suppress its second call
|
||||
if (bKilledFocus) {
|
||||
bKilledFocus = false;
|
||||
if (bKilledFocus)
|
||||
return;
|
||||
}
|
||||
bKilledFocus = true;
|
||||
#endif // __WXOSX__
|
||||
|
||||
|
|
@ -391,6 +390,10 @@ void TextCtrl::BUILD() {
|
|||
bEnterPressed = false;
|
||||
else
|
||||
propagate_value();
|
||||
#ifdef __WXOSX__
|
||||
// After processing of KILL_FOCUS event we should to invalidate a bKilledFocus flag
|
||||
bKilledFocus = false;
|
||||
#endif // __WXOSX__
|
||||
}), temp->GetId());
|
||||
|
||||
// select all text using Ctrl+A
|
||||
|
|
@ -1239,12 +1242,24 @@ void PointCtrl::msw_rescale(bool rescale_sidetext/* = false*/)
|
|||
y_textctrl->SetMinSize(field_size);
|
||||
}
|
||||
|
||||
bool PointCtrl::value_was_changed(wxTextCtrl* win)
|
||||
{
|
||||
if (m_value.empty())
|
||||
return true;
|
||||
|
||||
boost::any val = m_value;
|
||||
// update m_value!
|
||||
get_value();
|
||||
|
||||
return boost::any_cast<Vec2d>(m_value) != boost::any_cast<Vec2d>(val);
|
||||
}
|
||||
|
||||
void PointCtrl::propagate_value(wxTextCtrl* win)
|
||||
{
|
||||
if (!win->GetValue().empty())
|
||||
on_change_field();
|
||||
else
|
||||
if (win->GetValue().empty())
|
||||
on_kill_focus();
|
||||
else if (value_was_changed(win))
|
||||
on_change_field();
|
||||
}
|
||||
|
||||
void PointCtrl::set_value(const Vec2d& value, bool change_event)
|
||||
|
|
@ -1276,8 +1291,25 @@ void PointCtrl::set_value(const boost::any& value, bool change_event)
|
|||
boost::any& PointCtrl::get_value()
|
||||
{
|
||||
double x, y;
|
||||
x_textctrl->GetValue().ToDouble(&x);
|
||||
y_textctrl->GetValue().ToDouble(&y);
|
||||
if (!x_textctrl->GetValue().ToDouble(&x) ||
|
||||
!y_textctrl->GetValue().ToDouble(&y))
|
||||
{
|
||||
set_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true);
|
||||
show_error(m_parent, _L("Invalid numeric input."));
|
||||
}
|
||||
else
|
||||
if (m_opt.min > x || x > m_opt.max ||
|
||||
m_opt.min > y || y > m_opt.max)
|
||||
{
|
||||
if (m_opt.min > x) x = m_opt.min;
|
||||
if (x > m_opt.max) x = m_opt.max;
|
||||
if (m_opt.min > y) y = m_opt.min;
|
||||
if (y > m_opt.max) y = m_opt.max;
|
||||
set_value(Vec2d(x, y), true);
|
||||
|
||||
show_error(m_parent, _L("Input value is out of range"));
|
||||
}
|
||||
|
||||
return m_value = Vec2d(x, y);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ public:
|
|||
TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
|
||||
~TextCtrl() {}
|
||||
|
||||
void BUILD();
|
||||
void BUILD() override;
|
||||
bool value_was_changed();
|
||||
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value();
|
||||
|
|
@ -303,9 +303,9 @@ public:
|
|||
|
||||
void msw_rescale(bool rescale_sidetext = false) override;
|
||||
|
||||
virtual void enable();
|
||||
virtual void disable();
|
||||
virtual wxWindow* getWindow() { return window; }
|
||||
void enable() override;
|
||||
void disable() override;
|
||||
wxWindow* getWindow() override { return window; }
|
||||
};
|
||||
|
||||
class CheckBox : public Field {
|
||||
|
|
@ -445,6 +445,7 @@ public:
|
|||
wxTextCtrl* y_textctrl{ nullptr };
|
||||
|
||||
void BUILD() override;
|
||||
bool value_was_changed(wxTextCtrl* win);
|
||||
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value(wxTextCtrl* win);
|
||||
void set_value(const Vec2d& value, bool change_event = false);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
#include <chrono>
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#include "GLToolbar.hpp"
|
||||
|
|
@ -13,6 +16,9 @@
|
|||
#include "GUI_ObjectLayers.hpp"
|
||||
#include "GLSelectionRectangle.hpp"
|
||||
#include "MeshUtils.hpp"
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "Camera.hpp"
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#include <float.h>
|
||||
|
||||
|
|
@ -25,6 +31,9 @@ class wxMouseEvent;
|
|||
class wxTimerEvent;
|
||||
class wxPaintEvent;
|
||||
class wxGLCanvas;
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
class wxGLContext;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
// Support for Retina OpenGL on Mac OS
|
||||
#define ENABLE_RETINA_GL __APPLE__
|
||||
|
|
@ -35,9 +44,7 @@ class Bed3D;
|
|||
struct Camera;
|
||||
class BackgroundSlicingProcess;
|
||||
class GCodePreviewData;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
struct ThumbnailData;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
struct SlicingParameters;
|
||||
enum LayerHeightEditActionType : unsigned int;
|
||||
|
||||
|
|
@ -110,9 +117,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
|
|||
|
||||
class GLCanvas3D
|
||||
{
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
static const double DefaultCameraZoomToBoxMarginFactor;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
public:
|
||||
struct GCodePreviewVolumeIndex
|
||||
|
|
@ -386,6 +391,42 @@ private:
|
|||
void render(const std::vector<const ModelInstance*>& sorted_instances) const;
|
||||
};
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
class Tooltip
|
||||
{
|
||||
std::string m_text;
|
||||
std::chrono::steady_clock::time_point m_start_time;
|
||||
// Indicator that the mouse is inside an ImGUI dialog, therefore the tooltip should be suppressed.
|
||||
bool m_in_imgui = false;
|
||||
|
||||
public:
|
||||
bool is_empty() const { return m_text.empty(); }
|
||||
void set_text(const std::string& text);
|
||||
void render(const Vec2d& mouse_position, GLCanvas3D& canvas) const;
|
||||
// Indicates that the mouse is inside an ImGUI dialog, therefore the tooltip should be suppressed.
|
||||
void set_in_imgui(bool b) { m_in_imgui = b; }
|
||||
bool is_in_imgui() const { return m_in_imgui; }
|
||||
};
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
class Slope
|
||||
{
|
||||
bool m_enabled{ false };
|
||||
GLCanvas3D& m_canvas;
|
||||
GLVolumeCollection& m_volumes;
|
||||
|
||||
public:
|
||||
Slope(GLCanvas3D& canvas, GLVolumeCollection& volumes) : m_canvas(canvas), m_volumes(volumes) {}
|
||||
|
||||
void enable(bool enable) { m_enabled = enable; }
|
||||
bool is_enabled() const { return m_enabled; }
|
||||
void show(bool show) { m_volumes.set_slope_active(m_enabled ? show : false); }
|
||||
bool is_shown() const { return m_volumes.is_slope_active(); }
|
||||
void render() const;
|
||||
};
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
public:
|
||||
enum ECursorType : unsigned char
|
||||
{
|
||||
|
|
@ -403,9 +444,11 @@ private:
|
|||
LegendTexture m_legend_texture;
|
||||
WarningTexture m_warning_texture;
|
||||
wxTimer m_timer;
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
Bed3D& m_bed;
|
||||
Camera& m_camera;
|
||||
GLToolbar& m_view_toolbar;
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
LayersEditing m_layers_editing;
|
||||
Shader m_shader;
|
||||
Mouse m_mouse;
|
||||
|
|
@ -464,11 +507,25 @@ private:
|
|||
int m_selected_extruder;
|
||||
|
||||
Labels m_labels;
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
mutable Tooltip m_tooltip;
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
Slope m_slope;
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
public:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
explicit GLCanvas3D(wxGLCanvas* canvas);
|
||||
#else
|
||||
GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
~GLCanvas3D();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool is_initialized() const { return m_initialized; }
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
void set_context(wxGLContext* context) { m_context = context; }
|
||||
|
||||
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
||||
|
|
@ -515,9 +572,14 @@ public:
|
|||
|
||||
void set_color_by(const std::string& value);
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
void refresh_camera_scene_box();
|
||||
#else
|
||||
void refresh_camera_scene_box() { m_camera.set_scene_box(scene_bounding_box()); }
|
||||
const Camera& get_camera() const { return m_camera; }
|
||||
const Shader& get_shader() const { return m_shader; }
|
||||
Camera& get_camera() { return m_camera; }
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const Shader& get_shader() const { return m_shader; }
|
||||
|
||||
BoundingBoxf3 volumes_bounding_box() const;
|
||||
BoundingBoxf3 scene_bounding_box() const;
|
||||
|
|
@ -541,6 +603,9 @@ public:
|
|||
void enable_undoredo_toolbar(bool enable);
|
||||
void enable_dynamic_background(bool enable);
|
||||
void enable_labels(bool enable) { m_labels.enable(enable); }
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
void enable_slope(bool enable) { m_slope.enable(enable); }
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
void allow_multisample(bool allow);
|
||||
|
||||
void zoom_to_bed();
|
||||
|
|
@ -553,11 +618,9 @@ public:
|
|||
bool is_dragging() const { return m_gizmos.is_dragging() || m_moving; }
|
||||
|
||||
void render();
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
// printable_only == false -> render also non printable volumes as grayed
|
||||
// parts_only == false -> render also sla support and pad
|
||||
void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
void select_all();
|
||||
void deselect_all();
|
||||
|
|
@ -611,7 +674,9 @@ public:
|
|||
|
||||
void update_ui_from_settings();
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float get_view_toolbar_height() const { return m_view_toolbar.get_height(); }
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; }
|
||||
int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); }
|
||||
|
|
@ -643,7 +708,6 @@ public:
|
|||
Linef3 mouse_ray(const Point& mouse_pos);
|
||||
|
||||
void set_mouse_as_dragging() { m_mouse.dragging = true; }
|
||||
void refresh_camera_scene_box();
|
||||
bool is_mouse_dragging() const { return m_mouse.dragging; }
|
||||
|
||||
double get_size_proportional_to_max_bed_size(double factor) const;
|
||||
|
|
@ -665,6 +729,11 @@ public:
|
|||
bool are_labels_shown() const { return m_labels.is_shown(); }
|
||||
void show_labels(bool show) { m_labels.show(show); }
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
bool is_slope_shown() const { return m_slope.is_shown(); }
|
||||
void show_slope(bool show) { m_slope.show(show); }
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
|
||||
|
|
@ -678,11 +747,7 @@ private:
|
|||
|
||||
BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const;
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
void _zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultCameraZoomToBoxMarginFactor);
|
||||
#else
|
||||
void _zoom_to_box(const BoundingBoxf3& box);
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
void _update_camera_zoom(double zoom);
|
||||
|
||||
void _refresh_if_shown_on_screen();
|
||||
|
|
@ -710,8 +775,7 @@ private:
|
|||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
void _render_sla_slices() const;
|
||||
void _render_selection_sidebar_hints() const;
|
||||
void _render_undo_redo_stack(const bool is_undo, float pos_x) const;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
bool _render_undo_redo_stack(const bool is_undo, float pos_x) const;
|
||||
void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
// render thumbnail using an off-screen framebuffer
|
||||
void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
|
|
@ -719,7 +783,6 @@ private:
|
|||
void _render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
// render thumbnail using the default framebuffer
|
||||
void _render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
void _update_volumes_hover_state() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "GLCanvas3DManager.hpp"
|
||||
#include "../../slic3r/GUI/GUI.hpp"
|
||||
#include "../../slic3r/GUI/AppConfig.hpp"
|
||||
|
|
@ -7,7 +8,9 @@
|
|||
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include <boost/log/trivial.hpp>
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include <wx/glcanvas.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
|
@ -17,8 +20,10 @@
|
|||
#include <iostream>
|
||||
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
// Part of temporary hack to remove crash when closing on OSX 10.9.5
|
||||
#ifdef __APPLE__
|
||||
// Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets
|
||||
#include <wx/platinfo.h>
|
||||
#endif // __APPLE__
|
||||
#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
|
@ -28,6 +33,7 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3DManager::GLInfo::GLInfo()
|
||||
: m_detected(false)
|
||||
, m_version("")
|
||||
|
|
@ -38,6 +44,7 @@ GLCanvas3DManager::GLInfo::GLInfo()
|
|||
, m_max_anisotropy(0.0f)
|
||||
{
|
||||
}
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
const std::string& GLCanvas3DManager::GLInfo::get_version() const
|
||||
{
|
||||
|
|
@ -196,27 +203,57 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
|
|||
return out.str();
|
||||
}
|
||||
|
||||
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
||||
bool GLCanvas3DManager::s_compressed_textures_supported = false;
|
||||
GLCanvas3DManager::EFramebufferType GLCanvas3DManager::s_framebuffers_type = GLCanvas3DManager::FB_None;
|
||||
GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
|
||||
bool GLCanvas3DManager::s_compressed_textures_supported = false;
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::EMultisampleState::Unknown;
|
||||
GLCanvas3DManager::EFramebufferType GLCanvas3DManager::s_framebuffers_type = GLCanvas3DManager::EFramebufferType::Unknown;
|
||||
#else
|
||||
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
||||
GLCanvas3DManager::EFramebufferType GLCanvas3DManager::s_framebuffers_type = GLCanvas3DManager::FB_None;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
// Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets
|
||||
GLCanvas3DManager::OSInfo GLCanvas3DManager::s_os_info;
|
||||
#endif // __APPLE__
|
||||
#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3DManager::GLCanvas3DManager()
|
||||
: m_context(nullptr)
|
||||
, m_gl_initialized(false)
|
||||
{
|
||||
}
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
GLCanvas3DManager::~GLCanvas3DManager()
|
||||
{
|
||||
this->destroy();
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
// This is an ugly hack needed to solve the crash happening when closing the application on OSX 10.9.5 with newer wxWidgets
|
||||
// The crash is triggered inside wxGLContext destructor
|
||||
if (s_os_info.major != 10 || s_os_info.minor != 9 || s_os_info.micro != 5)
|
||||
{
|
||||
#endif //__APPLE__
|
||||
#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
|
||||
if (m_context != nullptr)
|
||||
delete m_context;
|
||||
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
}
|
||||
#endif //__APPLE__
|
||||
#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#else
|
||||
this->destroy();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
}
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
|
||||
{
|
||||
if (canvas == nullptr)
|
||||
|
|
@ -239,7 +276,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLTo
|
|||
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
// Part of temporary hack to remove crash when closing on OSX 10.9.5
|
||||
// Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets
|
||||
s_os_info.major = wxPlatformInfo::Get().GetOSMajorVersion();
|
||||
s_os_info.minor = wxPlatformInfo::Get().GetOSMinorVersion();
|
||||
s_os_info.micro = wxPlatformInfo::Get().GetOSMicroVersion();
|
||||
|
|
@ -277,28 +314,50 @@ void GLCanvas3DManager::remove_all()
|
|||
m_canvases.clear();
|
||||
}
|
||||
|
||||
unsigned int GLCanvas3DManager::count() const
|
||||
size_t GLCanvas3DManager::count() const
|
||||
{
|
||||
return (unsigned int)m_canvases.size();
|
||||
return m_canvases.size();
|
||||
}
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool GLCanvas3DManager::init_gl()
|
||||
#else
|
||||
void GLCanvas3DManager::init_gl()
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
{
|
||||
if (!m_gl_initialized)
|
||||
{
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
if (glewInit() != GLEW_OK)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to init glew library";
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
glewInit();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
m_gl_initialized = true;
|
||||
if (GLEW_EXT_texture_compression_s3tc)
|
||||
s_compressed_textures_supported = true;
|
||||
else
|
||||
s_compressed_textures_supported = false;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
if (GLEW_ARB_framebuffer_object)
|
||||
s_framebuffers_type = EFramebufferType::Arb;
|
||||
else if (GLEW_EXT_framebuffer_object)
|
||||
s_framebuffers_type = EFramebufferType::Ext;
|
||||
else
|
||||
s_framebuffers_type = EFramebufferType::Unknown;
|
||||
#else
|
||||
if (GLEW_ARB_framebuffer_object)
|
||||
s_framebuffers_type = FB_Arb;
|
||||
else if (GLEW_EXT_framebuffer_object)
|
||||
s_framebuffers_type = FB_Ext;
|
||||
else
|
||||
s_framebuffers_type = FB_None;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) {
|
||||
// Complain about the OpenGL version.
|
||||
|
|
@ -314,8 +373,31 @@ void GLCanvas3DManager::init_gl()
|
|||
wxMessageBox(message, wxString("PrusaSlicer - ") + _(L("Unsupported OpenGL version")), wxOK | wxICON_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
return true;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
}
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
wxGLContext* GLCanvas3DManager::init_glcontext(wxGLCanvas& canvas)
|
||||
{
|
||||
if (m_context == nullptr)
|
||||
{
|
||||
m_context = new wxGLContext(&canvas);
|
||||
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
// Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets
|
||||
s_os_info.major = wxPlatformInfo::Get().GetOSMajorVersion();
|
||||
s_os_info.minor = wxPlatformInfo::Get().GetOSMinorVersion();
|
||||
s_os_info.micro = wxPlatformInfo::Get().GetOSMicroVersion();
|
||||
#endif //__APPLE__
|
||||
#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
}
|
||||
return m_context;
|
||||
}
|
||||
#else
|
||||
bool GLCanvas3DManager::init(wxGLCanvas* canvas)
|
||||
{
|
||||
CanvasesMap::const_iterator it = do_get_canvas(canvas);
|
||||
|
|
@ -331,7 +413,7 @@ void GLCanvas3DManager::destroy()
|
|||
{
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
// this is a temporary ugly hack to solve the crash happening when closing the application on OSX 10.9.5
|
||||
// this is an ugly hack needed to solve the crash happening when closing the application on OSX 10.9.5
|
||||
// the crash is inside wxGLContext destructor
|
||||
if (s_os_info.major == 10 && s_os_info.minor == 9 && s_os_info.micro == 5)
|
||||
return;
|
||||
|
|
@ -342,14 +424,21 @@ void GLCanvas3DManager::destroy()
|
|||
m_context = nullptr;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas)
|
||||
{
|
||||
CanvasesMap::const_iterator it = do_get_canvas(canvas);
|
||||
return (it != m_canvases.end()) ? it->second : nullptr;
|
||||
}
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow& parent)
|
||||
#else
|
||||
wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
{
|
||||
int attribList[] = {
|
||||
WX_GL_RGBA,
|
||||
|
|
@ -367,7 +456,11 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
|
|||
0
|
||||
};
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
if (s_multisample == EMultisampleState::Unknown)
|
||||
#else
|
||||
if (s_multisample == MS_Unknown)
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
{
|
||||
detect_multisample(attribList);
|
||||
// // debug output
|
||||
|
|
@ -377,9 +470,14 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
|
|||
if (! can_multisample())
|
||||
attribList[12] = 0;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
return new wxGLCanvas(&parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
|
||||
#else
|
||||
return new wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
}
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas)
|
||||
{
|
||||
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
|
||||
|
|
@ -397,12 +495,17 @@ bool GLCanvas3DManager::init(GLCanvas3D& canvas)
|
|||
|
||||
return canvas.init();
|
||||
}
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
void GLCanvas3DManager::detect_multisample(int* attribList)
|
||||
{
|
||||
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
|
||||
bool enable_multisample = wxVersion >= 30003;
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? EMultisampleState::Enabled : EMultisampleState::Disabled;
|
||||
#else
|
||||
s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? MS_Enabled : MS_Disabled;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
// Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows
|
||||
// s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,22 +23,43 @@ class PrintObject;
|
|||
namespace GUI {
|
||||
|
||||
class GLCanvas3D;
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
class Bed3D;
|
||||
class GLToolbar;
|
||||
struct Camera;
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
class GLCanvas3DManager
|
||||
{
|
||||
public:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
enum class EFramebufferType : unsigned char
|
||||
{
|
||||
Unknown,
|
||||
Arb,
|
||||
Ext
|
||||
};
|
||||
#else
|
||||
enum EFramebufferType : unsigned char
|
||||
{
|
||||
FB_None,
|
||||
FB_Arb,
|
||||
FB_Ext
|
||||
};
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
class GLInfo
|
||||
{
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
mutable bool m_detected{ false };
|
||||
mutable int m_max_tex_size{ 0 };
|
||||
mutable float m_max_anisotropy{ 0.0f };
|
||||
|
||||
mutable std::string m_version;
|
||||
mutable std::string m_glsl_version;
|
||||
mutable std::string m_vendor;
|
||||
mutable std::string m_renderer;
|
||||
#else
|
||||
mutable bool m_detected;
|
||||
|
||||
mutable std::string m_version;
|
||||
|
|
@ -48,9 +69,14 @@ public:
|
|||
|
||||
mutable int m_max_tex_size;
|
||||
mutable float m_max_anisotropy;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
public:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLInfo() = default;
|
||||
#else
|
||||
GLInfo();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
const std::string& get_version() const;
|
||||
const std::string& get_glsl_version() const;
|
||||
|
|
@ -70,6 +96,7 @@ public:
|
|||
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
// Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets
|
||||
struct OSInfo
|
||||
{
|
||||
int major{ 0 };
|
||||
|
|
@ -80,6 +107,14 @@ public:
|
|||
#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
|
||||
private:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
enum class EMultisampleState : unsigned char
|
||||
{
|
||||
Unknown,
|
||||
Enabled,
|
||||
Disabled
|
||||
};
|
||||
#else
|
||||
enum EMultisampleState : unsigned char
|
||||
{
|
||||
MS_Unknown,
|
||||
|
|
@ -88,51 +123,85 @@ private:
|
|||
};
|
||||
|
||||
typedef std::map<wxGLCanvas*, GLCanvas3D*> CanvasesMap;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
CanvasesMap m_canvases;
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool m_gl_initialized{ false };
|
||||
wxGLContext* m_context{ nullptr };
|
||||
#else
|
||||
wxGLContext* m_context;
|
||||
bool m_gl_initialized;
|
||||
CanvasesMap m_canvases;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
static GLInfo s_gl_info;
|
||||
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
#ifdef __APPLE__
|
||||
// Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets
|
||||
static OSInfo s_os_info;
|
||||
#endif //__APPLE__
|
||||
#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5
|
||||
bool m_gl_initialized;
|
||||
static EMultisampleState s_multisample;
|
||||
static bool s_compressed_textures_supported;
|
||||
static EMultisampleState s_multisample;
|
||||
static EFramebufferType s_framebuffers_type;
|
||||
|
||||
public:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3DManager() = default;
|
||||
#else
|
||||
GLCanvas3DManager();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
~GLCanvas3DManager();
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
|
||||
bool remove(wxGLCanvas* canvas);
|
||||
void remove_all();
|
||||
|
||||
unsigned int count() const;
|
||||
size_t count() const;
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool init_gl();
|
||||
#else
|
||||
void init_gl();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
wxGLContext* init_glcontext(wxGLCanvas& canvas);
|
||||
#else
|
||||
bool init(wxGLCanvas* canvas);
|
||||
void destroy();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3D* get_canvas(wxGLCanvas* canvas);
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
static bool can_multisample() { return s_multisample == MS_Enabled; }
|
||||
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; }
|
||||
static bool are_framebuffers_supported() { return (s_framebuffers_type != EFramebufferType::Unknown); }
|
||||
#else
|
||||
static bool can_multisample() { return s_multisample == MS_Enabled; }
|
||||
static bool are_framebuffers_supported() { return (s_framebuffers_type != FB_None); }
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; }
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
static wxGLCanvas* create_wxglcanvas(wxWindow& parent);
|
||||
#else
|
||||
static wxGLCanvas* create_wxglcanvas(wxWindow *parent);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
static const GLInfo& get_gl_info() { return s_gl_info; }
|
||||
|
||||
private:
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
CanvasesMap::iterator do_get_canvas(wxGLCanvas* canvas);
|
||||
CanvasesMap::const_iterator do_get_canvas(wxGLCanvas* canvas) const;
|
||||
|
||||
bool init(GLCanvas3D& canvas);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
static void detect_multisample(int* attribList);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
#include "Camera.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "GUI_App.hpp"
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
|
|
@ -35,13 +38,17 @@ namespace GUI {
|
|||
|
||||
m_state = Off;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
#else
|
||||
const Camera& camera = canvas.get_camera();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
const Transform3d& modelview_matrix = camera.get_view_matrix();
|
||||
const Transform3d& projection_matrix = camera.get_projection_matrix();
|
||||
|
||||
// bounding box created from the rectangle corners - will take care of order of the corners
|
||||
BoundingBox rectangle(Points{ Point(m_start_corner.cast<int>()), Point(m_end_corner.cast<int>()) });
|
||||
BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) });
|
||||
|
||||
// Iterate over all points and determine whether they're in the rectangle.
|
||||
for (unsigned int i = 0; i<points.size(); ++i) {
|
||||
|
|
@ -68,7 +75,11 @@ namespace GUI {
|
|||
if (!is_dragging())
|
||||
return;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
#else
|
||||
const Camera& camera = canvas.get_camera();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)camera.get_inv_zoom();
|
||||
|
||||
Size cnv_size = canvas.get_canvas_size();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
#include "GLTexture.hpp"
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "GLCanvas3DManager.hpp"
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,14 @@
|
|||
|
||||
#include "GLToolbar.hpp"
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#else
|
||||
#include "../../slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "../../slic3r/GUI/Camera.hpp"
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/bitmap.h>
|
||||
|
|
@ -15,7 +21,6 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
|
||||
|
|
@ -85,7 +90,7 @@ bool GLToolbarItem::update_enabled_state()
|
|||
|
||||
void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
|
||||
{
|
||||
auto uvs = [this](unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) ->GLTexture::Quad_UVs
|
||||
auto uvs = [this](unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) -> GLTexture::Quad_UVs
|
||||
{
|
||||
assert((tex_width != 0) && (tex_height != 0));
|
||||
GLTexture::Quad_UVs ret;
|
||||
|
|
@ -153,7 +158,9 @@ GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name)
|
|||
, m_name(name)
|
||||
, m_enabled(false)
|
||||
, m_icons_texture_dirty(true)
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
, m_tooltip("")
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
, m_pressed_toggable_id(-1)
|
||||
{
|
||||
}
|
||||
|
|
@ -357,16 +364,37 @@ int GLToolbar::get_item_id(const std::string& name) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
std::string GLToolbar::get_tooltip() const
|
||||
{
|
||||
std::string tooltip;
|
||||
|
||||
for (GLToolbarItem* item : m_items)
|
||||
{
|
||||
if (item->is_hovered())
|
||||
{
|
||||
tooltip = item->get_tooltip();
|
||||
if (!item->is_pressed())
|
||||
{
|
||||
const std::string& additional_tooltip = item->get_additional_tooltip();
|
||||
if (!additional_tooltip.empty())
|
||||
tooltip += "\n" + additional_tooltip;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
void GLToolbar::get_additional_tooltip(int item_id, std::string& text)
|
||||
{
|
||||
if ((0 <= item_id) && (item_id < (int)m_items.size()))
|
||||
if (0 <= item_id && item_id < (int)m_items.size())
|
||||
{
|
||||
GLToolbarItem* item = m_items[item_id];
|
||||
if (item != nullptr)
|
||||
{
|
||||
text = item->get_additional_tooltip();
|
||||
return;
|
||||
}
|
||||
text = m_items[item_id]->get_additional_tooltip();
|
||||
return;
|
||||
}
|
||||
|
||||
text.clear();
|
||||
|
|
@ -374,12 +402,8 @@ void GLToolbar::get_additional_tooltip(int item_id, std::string& text)
|
|||
|
||||
void GLToolbar::set_additional_tooltip(int item_id, const std::string& text)
|
||||
{
|
||||
if ((0 <= item_id) && (item_id < (int)m_items.size()))
|
||||
{
|
||||
GLToolbarItem* item = m_items[item_id];
|
||||
if (item != nullptr)
|
||||
item->set_additional_tooltip(text);
|
||||
}
|
||||
if (0 <= item_id && item_id < (int)m_items.size())
|
||||
m_items[item_id]->set_additional_tooltip(text);
|
||||
}
|
||||
|
||||
bool GLToolbar::update_items_state()
|
||||
|
|
@ -420,14 +444,62 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
|
|||
// mouse anywhere
|
||||
if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr))
|
||||
{
|
||||
if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()))
|
||||
if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())) {
|
||||
// prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it,
|
||||
// as when switching between views
|
||||
processed = true;
|
||||
|
||||
m_mouse_capture.reset();
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
if (contains_mouse(mouse_pos, parent) == -1)
|
||||
// mouse is outside the toolbar
|
||||
m_tooltip.clear();
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
return true;
|
||||
}
|
||||
m_mouse_capture.reset();
|
||||
}
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
if (evt.Moving())
|
||||
update_hover_state(mouse_pos, parent);
|
||||
else if (evt.LeftUp())
|
||||
{
|
||||
if (m_mouse_capture.left)
|
||||
{
|
||||
processed = true;
|
||||
m_mouse_capture.left = false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (evt.MiddleUp())
|
||||
{
|
||||
if (m_mouse_capture.middle)
|
||||
{
|
||||
processed = true;
|
||||
m_mouse_capture.middle = false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (evt.RightUp())
|
||||
{
|
||||
if (m_mouse_capture.right)
|
||||
{
|
||||
processed = true;
|
||||
m_mouse_capture.right = false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (evt.Dragging())
|
||||
{
|
||||
if (m_mouse_capture.any())
|
||||
// if the button down was done on this toolbar, prevent from dragging into the scene
|
||||
processed = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (evt.Moving())
|
||||
m_tooltip = update_hover_state(mouse_pos, parent);
|
||||
else if (evt.LeftUp())
|
||||
|
|
@ -439,14 +511,19 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
|
|||
else if (evt.Dragging() && m_mouse_capture.any())
|
||||
// if the button down was done on this toolbar, prevent from dragging into the scene
|
||||
processed = true;
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
int item_id = contains_mouse(mouse_pos, parent);
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
if (item_id != -1)
|
||||
#else
|
||||
if (item_id == -1)
|
||||
{
|
||||
// mouse is outside the toolbar
|
||||
m_tooltip.clear();
|
||||
}
|
||||
else
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
{
|
||||
// mouse inside toolbar
|
||||
if (evt.LeftDown() || evt.LeftDClick())
|
||||
|
|
@ -478,8 +555,10 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
|
|||
parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
else if (evt.LeftUp())
|
||||
processed = true;
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
|
||||
return processed;
|
||||
|
|
@ -608,6 +687,20 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
switch (m_layout.type)
|
||||
{
|
||||
default:
|
||||
case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; }
|
||||
case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; }
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent)
|
||||
{
|
||||
if (!m_enabled)
|
||||
|
|
@ -620,12 +713,21 @@ std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& pa
|
|||
case Layout::Vertical: { return update_hover_state_vertical(mouse_pos, parent); }
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent)
|
||||
#else
|
||||
std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent)
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
{
|
||||
// NB: mouse_pos is already scaled appropriately
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float factor = m_layout.scale * inv_zoom;
|
||||
|
||||
Size cnv_size = parent.get_canvas_size();
|
||||
|
|
@ -642,8 +744,10 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
|
|||
float left = m_layout.left + scaled_border;
|
||||
float top = m_layout.top - scaled_border;
|
||||
|
||||
std::string tooltip = "";
|
||||
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
std::string tooltip;
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
for (GLToolbarItem* item : m_items)
|
||||
{
|
||||
if (!item->is_visible())
|
||||
|
|
@ -658,6 +762,7 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
|
|||
|
||||
GLToolbarItem::EState state = item->get_state();
|
||||
bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
if (inside)
|
||||
{
|
||||
tooltip = item->get_tooltip();
|
||||
|
|
@ -668,6 +773,7 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
|
|||
tooltip += "\n" + additional_tooltip;
|
||||
}
|
||||
}
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
switch (state)
|
||||
{
|
||||
|
|
@ -711,25 +817,62 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
|
|||
|
||||
break;
|
||||
}
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
case GLToolbarItem::Disabled:
|
||||
{
|
||||
if (inside)
|
||||
{
|
||||
item->set_state(GLToolbarItem::HoverDisabled);
|
||||
parent.set_as_dirty();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GLToolbarItem::HoverDisabled:
|
||||
{
|
||||
if (!inside)
|
||||
{
|
||||
item->set_state(GLToolbarItem::Disabled);
|
||||
parent.set_as_dirty();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
#else
|
||||
default:
|
||||
case GLToolbarItem::Disabled:
|
||||
{
|
||||
break;
|
||||
}
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
|
||||
left += icon_stride;
|
||||
}
|
||||
}
|
||||
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
return tooltip;
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent)
|
||||
#else
|
||||
std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent)
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
{
|
||||
// NB: mouse_pos is already scaled appropriately
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float factor = m_layout.scale * inv_zoom;
|
||||
|
||||
Size cnv_size = parent.get_canvas_size();
|
||||
|
|
@ -745,7 +888,9 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
|
|||
float left = m_layout.left + scaled_border;
|
||||
float top = m_layout.top - scaled_border;
|
||||
|
||||
std::string tooltip = "";
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
std::string tooltip;
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
for (GLToolbarItem* item : m_items)
|
||||
{
|
||||
|
|
@ -761,6 +906,7 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
|
|||
|
||||
GLToolbarItem::EState state = item->get_state();
|
||||
bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
if (inside)
|
||||
{
|
||||
tooltip = item->get_tooltip();
|
||||
|
|
@ -771,6 +917,7 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
|
|||
tooltip += "\n" + additional_tooltip;
|
||||
}
|
||||
}
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
switch (state)
|
||||
{
|
||||
|
|
@ -814,18 +961,47 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
|
|||
|
||||
break;
|
||||
}
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
case GLToolbarItem::Disabled:
|
||||
{
|
||||
if (inside)
|
||||
{
|
||||
item->set_state(GLToolbarItem::HoverDisabled);
|
||||
parent.set_as_dirty();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GLToolbarItem::HoverDisabled:
|
||||
{
|
||||
if (!inside)
|
||||
{
|
||||
item->set_state(GLToolbarItem::Disabled);
|
||||
parent.set_as_dirty();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
#else
|
||||
default:
|
||||
case GLToolbarItem::Disabled:
|
||||
{
|
||||
break;
|
||||
}
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
|
||||
top -= icon_stride;
|
||||
}
|
||||
}
|
||||
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
return tooltip;
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
|
||||
int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
|
||||
|
|
@ -845,7 +1021,11 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
|
|||
{
|
||||
// NB: mouse_pos is already scaled appropriately
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float factor = m_layout.scale * inv_zoom;
|
||||
|
||||
Size cnv_size = parent.get_canvas_size();
|
||||
|
|
@ -918,7 +1098,11 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
|
|||
{
|
||||
// NB: mouse_pos is already scaled appropriately
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float factor = m_layout.scale * inv_zoom;
|
||||
|
||||
Size cnv_size = parent.get_canvas_size();
|
||||
|
|
@ -1070,7 +1254,11 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
|
|||
int tex_width = m_icons_texture.get_width();
|
||||
int tex_height = m_icons_texture.get_height();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float factor = inv_zoom * m_layout.scale;
|
||||
|
||||
float scaled_icons_size = m_layout.icons_size * factor;
|
||||
|
|
@ -1118,7 +1306,11 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
|
|||
int tex_width = m_icons_texture.get_width();
|
||||
int tex_height = m_icons_texture.get_height();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float factor = inv_zoom * m_layout.scale;
|
||||
|
||||
float scaled_icons_size = m_layout.icons_size * factor;
|
||||
|
|
@ -1174,19 +1366,37 @@ bool GLToolbar::generate_icons_texture() const
|
|||
std::vector<std::pair<int, bool>> states;
|
||||
if (m_name == "Top")
|
||||
{
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
states.push_back({ 1, false }); // Normal
|
||||
states.push_back({ 0, false }); // Pressed
|
||||
states.push_back({ 2, false }); // Disabled
|
||||
states.push_back({ 0, false }); // Hover
|
||||
states.push_back({ 0, false }); // HoverPressed
|
||||
states.push_back({ 2, false }); // HoverDisabled
|
||||
#else
|
||||
states.push_back(std::make_pair(1, false));
|
||||
states.push_back(std::make_pair(0, false));
|
||||
states.push_back(std::make_pair(2, false));
|
||||
states.push_back(std::make_pair(0, false));
|
||||
states.push_back(std::make_pair(0, false));
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
else if (m_name == "View")
|
||||
{
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
states.push_back({ 1, false }); // Normal
|
||||
states.push_back({ 1, true }); // Pressed
|
||||
states.push_back({ 1, false }); // Disabled
|
||||
states.push_back({ 0, false }); // Hover
|
||||
states.push_back({ 1, true }); // HoverPressed
|
||||
states.push_back({ 1, false }); // HoverDisabled
|
||||
#else
|
||||
states.push_back(std::make_pair(1, false));
|
||||
states.push_back(std::make_pair(1, true));
|
||||
states.push_back(std::make_pair(1, false));
|
||||
states.push_back(std::make_pair(0, false));
|
||||
states.push_back(std::make_pair(1, true));
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
|
||||
unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale);
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ public:
|
|||
Disabled,
|
||||
Hover,
|
||||
HoverPressed,
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
HoverDisabled,
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
Num_States
|
||||
};
|
||||
|
||||
|
|
@ -119,9 +122,15 @@ public:
|
|||
void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); }
|
||||
void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); }
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
bool is_enabled() const { return (m_state != Disabled) && (m_state != HoverDisabled); }
|
||||
bool is_disabled() const { return (m_state == Disabled) || (m_state == HoverDisabled); }
|
||||
bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed) || (m_state == HoverDisabled); }
|
||||
#else
|
||||
bool is_enabled() const { return m_state != Disabled; }
|
||||
bool is_disabled() const { return m_state == Disabled; }
|
||||
bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed); }
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); }
|
||||
bool is_visible() const { return m_data.visible; }
|
||||
bool is_separator() const { return m_type == Separator; }
|
||||
|
|
@ -252,7 +261,9 @@ private:
|
|||
};
|
||||
|
||||
MouseCapture m_mouse_capture;
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
std::string m_tooltip;
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
int m_pressed_toggable_id;
|
||||
|
||||
public:
|
||||
|
|
@ -298,7 +309,11 @@ public:
|
|||
void force_left_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Left, item_id, parent, false); }
|
||||
void force_right_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Right, item_id, parent, false); }
|
||||
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
std::string get_tooltip() const;
|
||||
#else
|
||||
const std::string& get_tooltip() const { return m_tooltip; }
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
void get_additional_tooltip(int item_id, std::string& text);
|
||||
void set_additional_tooltip(int item_id, const std::string& text);
|
||||
|
|
@ -318,9 +333,15 @@ private:
|
|||
float get_height_vertical() const;
|
||||
float get_main_size() const;
|
||||
void do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover);
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
#else
|
||||
std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
// returns the id of the item under the given mouse position or -1 if none
|
||||
int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef 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>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/imagpng.h>
|
||||
|
|
@ -46,10 +47,12 @@
|
|||
#include "SysInfoDialog.hpp"
|
||||
#include "KBShortcutsDialog.hpp"
|
||||
#include "UpdateDialogs.hpp"
|
||||
#include "Mouse3DController.hpp"
|
||||
#include "RemovableDriveManager.hpp"
|
||||
|
||||
#ifdef __WXMSW__
|
||||
#include <Shlobj.h>
|
||||
#include <dbt.h>
|
||||
#include <shlobj.h>
|
||||
#endif // __WXMSW__
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||
|
|
@ -60,6 +63,7 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class MainFrame;
|
||||
|
||||
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
{
|
||||
|
|
@ -96,9 +100,9 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
|||
|
||||
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
|
||||
|
||||
static void register_dpi_event()
|
||||
{
|
||||
#ifdef WIN32
|
||||
static void register_win32_dpi_event()
|
||||
{
|
||||
enum { WM_DPICHANGED_ = 0x02e0 };
|
||||
|
||||
wxWindow::MSWRegisterMessageHandler(WM_DPICHANGED_, [](wxWindow *win, WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) {
|
||||
|
|
@ -111,9 +115,101 @@ static void register_dpi_event()
|
|||
|
||||
return true;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
|
||||
|
||||
static void register_win32_device_notification_event()
|
||||
{
|
||||
enum { WM_DPICHANGED_ = 0x02e0 };
|
||||
|
||||
wxWindow::MSWRegisterMessageHandler(WM_DEVICECHANGE, [](wxWindow *win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
|
||||
// Some messages are sent to top level windows by default, some messages are sent to only registered windows, and we explictely register on MainFrame only.
|
||||
auto main_frame = dynamic_cast<MainFrame*>(win);
|
||||
auto plater = (main_frame == nullptr) ? nullptr : main_frame->plater();
|
||||
if (plater == nullptr)
|
||||
// Maybe some other top level window like a dialog or maybe a pop-up menu?
|
||||
return true;
|
||||
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
|
||||
switch (wParam) {
|
||||
case DBT_DEVICEARRIVAL:
|
||||
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeAttachedEvent(EVT_VOLUME_ATTACHED));
|
||||
else if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
PDEV_BROADCAST_DEVICEINTERFACE lpdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
|
||||
// if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_VOLUME) {
|
||||
// printf("DBT_DEVICEARRIVAL %d - Media has arrived: %ws\n", msg_count, lpdbi->dbcc_name);
|
||||
if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
plater->GetEventHandler()->AddPendingEvent(HIDDeviceAttachedEvent(EVT_HID_DEVICE_ATTACHED, boost::nowide::narrow(lpdbi->dbcc_name)));
|
||||
}
|
||||
break;
|
||||
case DBT_DEVICEREMOVECOMPLETE:
|
||||
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeDetachedEvent(EVT_VOLUME_DETACHED));
|
||||
else if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
PDEV_BROADCAST_DEVICEINTERFACE lpdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
|
||||
// if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_VOLUME)
|
||||
// printf("DBT_DEVICEARRIVAL %d - Media was removed: %ws\n", msg_count, lpdbi->dbcc_name);
|
||||
if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
plater->GetEventHandler()->AddPendingEvent(HIDDeviceDetachedEvent(EVT_HID_DEVICE_DETACHED, boost::nowide::narrow(lpdbi->dbcc_name)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
wxWindow::MSWRegisterMessageHandler(MainFrame::WM_USER_MEDIACHANGED, [](wxWindow *win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
|
||||
// Some messages are sent to top level windows by default, some messages are sent to only registered windows, and we explictely register on MainFrame only.
|
||||
auto main_frame = dynamic_cast<MainFrame*>(win);
|
||||
auto plater = (main_frame == nullptr) ? nullptr : main_frame->plater();
|
||||
if (plater == nullptr)
|
||||
// Maybe some other top level window like a dialog or maybe a pop-up menu?
|
||||
return true;
|
||||
wchar_t sPath[MAX_PATH];
|
||||
if (lParam == SHCNE_MEDIAINSERTED || lParam == SHCNE_MEDIAREMOVED) {
|
||||
struct _ITEMIDLIST* pidl = *reinterpret_cast<struct _ITEMIDLIST**>(wParam);
|
||||
if (! SHGetPathFromIDList(pidl, sPath)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "MediaInserted: SHGetPathFromIDList failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (lParam) {
|
||||
case SHCNE_MEDIAINSERTED:
|
||||
{
|
||||
//printf("SHCNE_MEDIAINSERTED %S\n", sPath);
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeAttachedEvent(EVT_VOLUME_ATTACHED));
|
||||
break;
|
||||
}
|
||||
case SHCNE_MEDIAREMOVED:
|
||||
{
|
||||
//printf("SHCNE_MEDIAREMOVED %S\n", sPath);
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeDetachedEvent(EVT_VOLUME_DETACHED));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// printf("Unknown\n");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
wxWindow::MSWRegisterMessageHandler(WM_INPUT, [](wxWindow *win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
|
||||
auto main_frame = dynamic_cast<MainFrame*>(Slic3r::GUI::find_toplevel_parent(win));
|
||||
auto plater = (main_frame == nullptr) ? nullptr : main_frame->plater();
|
||||
// if (wParam == RIM_INPUTSINK && plater != nullptr && main_frame->IsActive()) {
|
||||
if (wParam == RIM_INPUT && plater != nullptr && main_frame->IsActive()) {
|
||||
RAWINPUT raw;
|
||||
UINT rawSize = sizeof(RAWINPUT);
|
||||
::GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &raw, &rawSize, sizeof(RAWINPUTHEADER));
|
||||
if (raw.header.dwType == RIM_TYPEHID && plater->get_mouse3d_controller().handle_raw_input_win32(raw.data.hid.bRawData, raw.data.hid.dwSizeHid))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
#endif // WIN32
|
||||
|
||||
static void generic_exception_handle()
|
||||
{
|
||||
|
|
@ -155,6 +251,7 @@ GUI_App::GUI_App()
|
|||
, m_em_unit(10)
|
||||
, m_imgui(new ImGuiWrapper())
|
||||
, m_wizard(nullptr)
|
||||
, m_removable_drive_manager(std::make_unique<RemovableDriveManager>())
|
||||
{}
|
||||
|
||||
GUI_App::~GUI_App()
|
||||
|
|
@ -169,6 +266,23 @@ GUI_App::~GUI_App()
|
|||
delete preset_updater;
|
||||
}
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
std::string GUI_App::get_gl_info(bool format_as_html, bool extensions)
|
||||
{
|
||||
return GLCanvas3DManager::get_gl_info().to_string(format_as_html, extensions);
|
||||
}
|
||||
|
||||
wxGLContext* GUI_App::init_glcontext(wxGLCanvas& canvas)
|
||||
{
|
||||
return m_canvas_mgr.init_glcontext(canvas);
|
||||
}
|
||||
|
||||
bool GUI_App::init_opengl()
|
||||
{
|
||||
return m_canvas_mgr.init_gl();
|
||||
}
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
bool GUI_App::OnInit()
|
||||
{
|
||||
try {
|
||||
|
|
@ -247,7 +361,10 @@ bool GUI_App::on_init_inner()
|
|||
show_error(nullptr, ex.what());
|
||||
}
|
||||
|
||||
register_dpi_event();
|
||||
#ifdef WIN32
|
||||
register_win32_dpi_event();
|
||||
register_win32_device_notification_event();
|
||||
#endif // WIN32
|
||||
|
||||
// Let the libslic3r know the callback, which will translate messages on demand.
|
||||
Slic3r::I18N::set_translate_callback(libslic3r_translate_callback);
|
||||
|
|
@ -262,7 +379,6 @@ bool GUI_App::on_init_inner()
|
|||
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
|
||||
RemovableDriveManager::get_instance().init();
|
||||
|
||||
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
|
||||
{
|
||||
|
|
@ -274,10 +390,6 @@ bool GUI_App::on_init_inner()
|
|||
|
||||
this->obj_manipul()->update_if_dirty();
|
||||
|
||||
#if !__APPLE__
|
||||
RemovableDriveManager::get_instance().update(wxGetLocalTime(), true);
|
||||
#endif
|
||||
|
||||
// Preset updating & Configwizard are done after the above initializations,
|
||||
// and after MainFrame is created & shown.
|
||||
// The extra CallAfter() is needed because of Mac, where this is the only way
|
||||
|
|
@ -437,46 +549,30 @@ float GUI_App::toolbar_icon_scale(const bool is_limited/* = false*/) const
|
|||
|
||||
void GUI_App::recreate_GUI()
|
||||
{
|
||||
// Weird things happen as the Paint messages are floating around the windows being destructed.
|
||||
// Avoid the Paint messages by hiding the main window.
|
||||
// Also the application closes much faster without these unnecessary screen refreshes.
|
||||
// In addition, there were some crashes due to the Paint events sent to already destructed windows.
|
||||
mainframe->Show(false);
|
||||
mainframe->shutdown();
|
||||
|
||||
const auto msg_name = _(L("Changing of an application language")) + dots;
|
||||
wxProgressDialog dlg(msg_name, msg_name);
|
||||
dlg.Pulse();
|
||||
|
||||
// to make sure nobody accesses data from the soon-to-be-destroyed widgets:
|
||||
tabs_list.clear();
|
||||
plater_ = nullptr;
|
||||
|
||||
dlg.Update(10, _(L("Recreating")) + dots);
|
||||
|
||||
MainFrame* topwindow = mainframe;
|
||||
MainFrame *old_main_frame = mainframe;
|
||||
mainframe = new MainFrame();
|
||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||
// Propagate model objects to object list.
|
||||
sidebar().obj_list()->init_objects();
|
||||
SetTopWindow(mainframe);
|
||||
|
||||
if (topwindow) {
|
||||
SetTopWindow(mainframe);
|
||||
|
||||
dlg.Update(30, _(L("Recreating")) + dots);
|
||||
topwindow->Destroy();
|
||||
|
||||
// For this moment ConfigWizard is deleted, invalidate it
|
||||
m_wizard = nullptr;
|
||||
}
|
||||
dlg.Update(30, _(L("Recreating")) + dots);
|
||||
old_main_frame->Destroy();
|
||||
// For this moment ConfigWizard is deleted, invalidate it.
|
||||
m_wizard = nullptr;
|
||||
|
||||
dlg.Update(80, _(L("Loading of current presets")) + dots);
|
||||
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
|
||||
load_current_presets();
|
||||
|
||||
mainframe->Show(true);
|
||||
|
||||
dlg.Update(90, _(L("Loading of a mode view")) + dots);
|
||||
|
||||
/* Temporary workaround for the correct behavior of the Scrolled sidebar panel:
|
||||
* change min hight of object list to the normal min value (15 * wxGetApp().em_unit())
|
||||
* after first whole Mainframe updating/layouting
|
||||
|
|
@ -484,7 +580,6 @@ void GUI_App::recreate_GUI()
|
|||
const int list_min_height = 15 * em_unit();
|
||||
if (obj_list()->GetMinSize().GetY() > list_min_height)
|
||||
obj_list()->SetMinSize(wxSize(-1, list_min_height));
|
||||
|
||||
update_mode();
|
||||
|
||||
// #ys_FIXME_delete_after_testing Do we still need this ?
|
||||
|
|
@ -566,6 +661,12 @@ void GUI_App::load_project(wxWindow *parent, wxString& input_file) const
|
|||
|
||||
void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const
|
||||
{
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
if (this->plater_ != nullptr)
|
||||
// hides the tooltip
|
||||
plater_->get_current_canvas3D()->set_tooltip("");
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
input_files.Clear();
|
||||
wxFileDialog dialog(parent ? parent : GetTopWindow(),
|
||||
_(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")),
|
||||
|
|
@ -579,7 +680,6 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const
|
|||
bool GUI_App::switch_language()
|
||||
{
|
||||
if (select_language()) {
|
||||
_3DScene::remove_all_canvases();
|
||||
recreate_GUI();
|
||||
return true;
|
||||
} else {
|
||||
|
|
@ -634,15 +734,16 @@ bool GUI_App::select_language()
|
|||
// Try to load a new language.
|
||||
if (index != -1 && (init_selection == -1 || init_selection != index)) {
|
||||
const wxLanguageInfo *new_language_info = language_infos[index];
|
||||
if (new_language_info == m_language_info_best || new_language_info == m_language_info_system) {
|
||||
// The newly selected profile matches user's default profile exactly. That's great.
|
||||
} else if (m_language_info_best != nullptr && new_language_info->CanonicalName.BeforeFirst('_') == m_language_info_best->CanonicalName.BeforeFirst('_'))
|
||||
new_language_info = m_language_info_best;
|
||||
else if (m_language_info_system != nullptr && new_language_info->CanonicalName.BeforeFirst('_') == m_language_info_system->CanonicalName.BeforeFirst('_'))
|
||||
new_language_info = m_language_info_system;
|
||||
if (this->load_language(new_language_info->CanonicalName, false)) {
|
||||
// Save language at application config.
|
||||
app_config->set("translation_language", m_wxLocale->GetCanonicalName().ToUTF8().data());
|
||||
// Which language to save as the selected dictionary language?
|
||||
// 1) Hopefully the language set to wxTranslations by this->load_language(), but that API is weird and we don't want to rely on its
|
||||
// stability in the future:
|
||||
// wxTranslations::Get()->GetBestTranslation(SLIC3R_APP_KEY, wxLANGUAGE_ENGLISH);
|
||||
// 2) Current locale language may not match the dictionary name, see GH issue #3901
|
||||
// m_wxLocale->GetCanonicalName()
|
||||
// 3) new_language_info->CanonicalName is a safe bet. It points to a valid dictionary name.
|
||||
app_config->set("translation_language", new_language_info->CanonicalName.ToUTF8().data());
|
||||
app_config->save();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -671,7 +772,6 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
BOOST_LOG_TRIVIAL(trace) << boost::format("System language detected (user locales and such): %1%") % m_language_info_system->CanonicalName.ToUTF8().data();
|
||||
}
|
||||
}
|
||||
#if defined(__WXMSW__) || defined(__WXOSX__)
|
||||
{
|
||||
// Allocating a temporary locale will switch the default wxTranslations to its internal wxTranslations instance.
|
||||
wxLocale temp_locale;
|
||||
|
|
@ -688,7 +788,6 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
BOOST_LOG_TRIVIAL(trace) << boost::format("Best translation language detected (may be different from user locales): %1%") % m_language_info_best->CanonicalName.ToUTF8().data();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const wxLanguageInfo *language_info = language.empty() ? nullptr : wxLocale::FindLanguageInfo(language);
|
||||
|
|
@ -704,6 +803,7 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
}
|
||||
|
||||
if (language_info == nullptr) {
|
||||
// PrusaSlicer does not support the Right to Left languages yet.
|
||||
if (m_language_info_system != nullptr && m_language_info_system->LayoutDirection != wxLayout_RightToLeft)
|
||||
language_info = m_language_info_system;
|
||||
if (m_language_info_best != nullptr && m_language_info_best->LayoutDirection != wxLayout_RightToLeft)
|
||||
|
|
@ -722,6 +822,16 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
BOOST_LOG_TRIVIAL(trace) << "Using Czech dictionaries for Slovak language";
|
||||
}
|
||||
|
||||
// Select language for locales. This language may be different from the language of the dictionary.
|
||||
if (language_info == m_language_info_best || language_info == m_language_info_system) {
|
||||
// The current language matches user's default profile exactly. That's great.
|
||||
} else if (m_language_info_best != nullptr && language_info->CanonicalName.BeforeFirst('_') == m_language_info_best->CanonicalName.BeforeFirst('_')) {
|
||||
// Use whatever the operating system recommends, if it the language code of the dictionary matches the recommended language.
|
||||
// This allows a Swiss guy to use a German dictionary without forcing him to German locales.
|
||||
language_info = m_language_info_best;
|
||||
} else if (m_language_info_system != nullptr && language_info->CanonicalName.BeforeFirst('_') == m_language_info_system->CanonicalName.BeforeFirst('_'))
|
||||
language_info = m_language_info_system;
|
||||
|
||||
if (! wxLocale::IsAvailable(language_info->Language)) {
|
||||
// Loading the language dictionary failed.
|
||||
wxString message = "Switching PrusaSlicer to language " + language_info->CanonicalName + " failed.";
|
||||
|
|
@ -748,7 +858,7 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
wxTranslations::Get()->SetLanguage(language_dict);
|
||||
m_wxLocale->AddCatalog(SLIC3R_APP_KEY);
|
||||
m_imgui->set_language(into_u8(language_info->CanonicalName));
|
||||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
wxSetlocale(LC_NUMERIC, "C");
|
||||
Preset::update_suffix_modified();
|
||||
return true;
|
||||
|
|
@ -882,14 +992,19 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
/* Before change application language, let's check unsaved changes on 3D-Scene
|
||||
* and draw user's attention to the application restarting after a language change
|
||||
*/
|
||||
wxMessageDialog dialog(nullptr,
|
||||
_(L("Switching the language will trigger application restart.\n"
|
||||
"You will lose content of the plater.")) + "\n\n" +
|
||||
_(L("Do you want to proceed?")),
|
||||
wxString(SLIC3R_APP_NAME) + " - " + _(L("Language selection")),
|
||||
wxICON_QUESTION | wxOK | wxCANCEL);
|
||||
if ( dialog.ShowModal() == wxID_CANCEL)
|
||||
return;
|
||||
{
|
||||
// the dialog needs to be destroyed before the call to switch_language()
|
||||
// or sometimes the application crashes into wxDialogBase() destructor
|
||||
// so we put it into an inner scope
|
||||
wxMessageDialog dialog(nullptr,
|
||||
_(L("Switching the language will trigger application restart.\n"
|
||||
"You will lose content of the plater.")) + "\n\n" +
|
||||
_(L("Do you want to proceed?")),
|
||||
wxString(SLIC3R_APP_NAME) + " - " + _(L("Language selection")),
|
||||
wxICON_QUESTION | wxOK | wxCANCEL);
|
||||
if (dialog.ShowModal() == wxID_CANCEL)
|
||||
return;
|
||||
}
|
||||
|
||||
switch_language();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#include "MainFrame.hpp"
|
||||
#include "ImGuiWrapper.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "GLCanvas3DManager.hpp"
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/colour.h>
|
||||
|
|
@ -29,9 +32,9 @@ class PresetUpdater;
|
|||
class ModelObject;
|
||||
class PrintHostJobQueue;
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
|
||||
namespace GUI{
|
||||
class RemovableDriveManager;
|
||||
enum FileType
|
||||
{
|
||||
FT_STL,
|
||||
|
|
@ -96,6 +99,12 @@ class GUI_App : public wxApp
|
|||
// Best translation language, provided by Windows or OSX, owned by wxWidgets.
|
||||
const wxLanguageInfo *m_language_info_best = nullptr;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
GLCanvas3DManager m_canvas_mgr;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
std::unique_ptr<RemovableDriveManager> m_removable_drive_manager;
|
||||
|
||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
||||
ConfigWizard* m_wizard; // Managed by wxWindow tree
|
||||
|
|
@ -107,6 +116,12 @@ public:
|
|||
GUI_App();
|
||||
~GUI_App() override;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
static std::string get_gl_info(bool format_as_html, bool extensions);
|
||||
wxGLContext* init_glcontext(wxGLCanvas& canvas);
|
||||
bool init_opengl();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
static unsigned get_colour_approx_luma(const wxColour &colour);
|
||||
static bool dark_mode();
|
||||
void init_label_colours();
|
||||
|
|
@ -180,6 +195,8 @@ public:
|
|||
|
||||
std::vector<Tab *> tabs_list;
|
||||
|
||||
RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); }
|
||||
|
||||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||
|
||||
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_ed
|
|||
}
|
||||
}
|
||||
|
||||
wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
|
||||
wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinusButton *delete_button, PlusMinusButton *add_button)
|
||||
{
|
||||
const bool is_last_edited_range = range == m_selectable_range;
|
||||
|
||||
|
|
@ -79,8 +79,8 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
|
|||
|
||||
// Add control for the "Min Z"
|
||||
|
||||
auto editor = new LayerRangeEditor(this, double_to_string(range.first), etMinZ,
|
||||
set_focus_data, [range, update_focus_data, this](coordf_t min_z, bool enter_pressed)
|
||||
auto editor = new LayerRangeEditor(this, double_to_string(range.first), etMinZ, set_focus_data,
|
||||
[range, update_focus_data, this, delete_button, add_button](coordf_t min_z, bool enter_pressed, bool dont_update_ui)
|
||||
{
|
||||
if (fabs(min_z - range.first) < EPSILON) {
|
||||
m_selection_type = etUndef;
|
||||
|
|
@ -89,10 +89,14 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
|
|||
|
||||
// data for next focusing
|
||||
coordf_t max_z = min_z < range.second ? range.second : min_z + 0.5;
|
||||
const t_layer_height_range& new_range = { min_z, max_z };
|
||||
const t_layer_height_range new_range = { min_z, max_z };
|
||||
if (delete_button)
|
||||
delete_button->range = new_range;
|
||||
if (add_button)
|
||||
add_button->range = new_range;
|
||||
update_focus_data(new_range, etMinZ, enter_pressed);
|
||||
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, new_range);
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, new_range, dont_update_ui);
|
||||
});
|
||||
|
||||
select_editor(editor, is_last_edited_range);
|
||||
|
|
@ -100,8 +104,8 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
|
|||
|
||||
// Add control for the "Max Z"
|
||||
|
||||
editor = new LayerRangeEditor(this, double_to_string(range.second), etMaxZ,
|
||||
set_focus_data, [range, update_focus_data, this](coordf_t max_z, bool enter_pressed)
|
||||
editor = new LayerRangeEditor(this, double_to_string(range.second), etMaxZ, set_focus_data,
|
||||
[range, update_focus_data, this, delete_button, add_button](coordf_t max_z, bool enter_pressed, bool dont_update_ui)
|
||||
{
|
||||
if (fabs(max_z - range.second) < EPSILON || range.first > max_z) {
|
||||
m_selection_type = etUndef;
|
||||
|
|
@ -110,9 +114,13 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
|
|||
|
||||
// data for next focusing
|
||||
const t_layer_height_range& new_range = { range.first, max_z };
|
||||
if (delete_button)
|
||||
delete_button->range = new_range;
|
||||
if (add_button)
|
||||
add_button->range = new_range;
|
||||
update_focus_data(new_range, etMaxZ, enter_pressed);
|
||||
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, new_range);
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, new_range, dont_update_ui);
|
||||
});
|
||||
|
||||
select_editor(editor, is_last_edited_range);
|
||||
|
|
@ -120,9 +128,8 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
|
|||
|
||||
// Add control for the "Layer height"
|
||||
|
||||
editor = new LayerRangeEditor(this,
|
||||
double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()),
|
||||
etLayerHeight, set_focus_data, [range, this](coordf_t layer_height, bool)
|
||||
editor = new LayerRangeEditor(this, double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()), etLayerHeight, set_focus_data,
|
||||
[range, this](coordf_t layer_height, bool, bool)
|
||||
{
|
||||
return wxGetApp().obj_list()->edit_layer_range(range, layer_height);
|
||||
});
|
||||
|
|
@ -141,30 +148,30 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
|
|||
|
||||
return sizer;
|
||||
}
|
||||
|
||||
|
||||
void ObjectLayers::create_layers_list()
|
||||
{
|
||||
for (const auto layer : m_object->layer_config_ranges)
|
||||
{
|
||||
const t_layer_height_range& range = layer.first;
|
||||
auto sizer = create_layer(range);
|
||||
|
||||
auto del_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_delete);
|
||||
auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range);
|
||||
del_btn->SetToolTip(_(L("Remove layer range")));
|
||||
|
||||
auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range);
|
||||
wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range);
|
||||
add_btn->SetToolTip(tooltip.IsEmpty() ? _(L("Add layer range")) : tooltip);
|
||||
add_btn->Enable(tooltip.IsEmpty());
|
||||
|
||||
auto sizer = create_layer(range, del_btn, add_btn);
|
||||
sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
|
||||
|
||||
del_btn->Bind(wxEVT_BUTTON, [range](wxEvent &) {
|
||||
wxGetApp().obj_list()->del_layer_range(range);
|
||||
});
|
||||
|
||||
auto add_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_add);
|
||||
add_btn->SetToolTip(_(L("Add layer range")));
|
||||
|
||||
sizer->Add(add_btn);
|
||||
|
||||
add_btn->Bind(wxEVT_BUTTON, [range](wxEvent &) {
|
||||
wxGetApp().obj_list()->add_layer_range_after_current(range);
|
||||
del_btn->Bind(wxEVT_BUTTON, [del_btn](wxEvent &) {
|
||||
wxGetApp().obj_list()->del_layer_range(del_btn->range);
|
||||
});
|
||||
|
||||
add_btn->Bind(wxEVT_BUTTON, [add_btn](wxEvent &) {
|
||||
wxGetApp().obj_list()->add_layer_range_after_current(add_btn->range);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -204,7 +211,7 @@ void ObjectLayers::update_layers_list()
|
|||
if (type & itLayerRoot)
|
||||
create_layers_list();
|
||||
else
|
||||
create_layer(objects_ctrl->GetModel()->GetLayerRangeByItem(item));
|
||||
create_layer(objects_ctrl->GetModel()->GetLayerRangeByItem(item), nullptr, nullptr);
|
||||
|
||||
m_parent->Layout();
|
||||
}
|
||||
|
|
@ -255,7 +262,7 @@ void ObjectLayers::msw_rescale()
|
|||
{
|
||||
wxSizerItem* b_item = item->GetSizer()->GetItem(btn);
|
||||
if (b_item->IsWindow()) {
|
||||
ScalableButton* button = dynamic_cast<ScalableButton*>(b_item->GetWindow());
|
||||
auto button = dynamic_cast<PlusMinusButton*>(b_item->GetWindow());
|
||||
if (button != nullptr)
|
||||
button->msw_rescale();
|
||||
}
|
||||
|
|
@ -275,7 +282,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
|
|||
const wxString& value,
|
||||
EditorType type,
|
||||
std::function<void(EditorType)> set_focus_data_fn,
|
||||
std::function<bool(coordf_t, bool enter_pressed)> edit_fn
|
||||
std::function<bool(coordf_t, bool, bool)> edit_fn
|
||||
) :
|
||||
m_valid_value(value),
|
||||
m_type(type),
|
||||
|
|
@ -293,13 +300,13 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
|
|||
m_enter_pressed = true;
|
||||
// If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
|
||||
if (m_type&etLayerHeight) {
|
||||
if (!edit_fn(get_value(), true))
|
||||
if (!edit_fn(get_value(), true, false))
|
||||
SetValue(m_valid_value);
|
||||
else
|
||||
m_valid_value = double_to_string(get_value());
|
||||
m_call_kill_focus = true;
|
||||
}
|
||||
else if (!edit_fn(get_value(), true)) {
|
||||
else if (!edit_fn(get_value(), true, false)) {
|
||||
SetValue(m_valid_value);
|
||||
m_call_kill_focus = true;
|
||||
}
|
||||
|
|
@ -319,13 +326,13 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
|
|||
#endif // not __WXGTK__
|
||||
// If LayersList wasn't updated/recreated, we should call e.Skip()
|
||||
if (m_type & etLayerHeight) {
|
||||
if (!edit_fn(get_value(), false))
|
||||
if (!edit_fn(get_value(), false, dynamic_cast<ObjectLayers::PlusMinusButton*>(e.GetWindow()) != nullptr))
|
||||
SetValue(m_valid_value);
|
||||
else
|
||||
m_valid_value = double_to_string(get_value());
|
||||
e.Skip();
|
||||
}
|
||||
else if (!edit_fn(get_value(), false)) {
|
||||
else if (!edit_fn(get_value(), false, dynamic_cast<ObjectLayers::PlusMinusButton*>(e.GetWindow()) != nullptr)) {
|
||||
SetValue(m_valid_value);
|
||||
e.Skip();
|
||||
}
|
||||
|
|
@ -387,4 +394,4 @@ void LayerRangeEditor::msw_rescale()
|
|||
}
|
||||
|
||||
} //namespace GUI
|
||||
} //namespace Slic3r
|
||||
} //namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ public:
|
|||
const wxString& value = wxEmptyString,
|
||||
EditorType type = etUndef,
|
||||
std::function<void(EditorType)> set_focus_data_fn = [](EditorType) {;},
|
||||
std::function<bool(coordf_t, bool)> edit_fn = [](coordf_t, bool) {return false; }
|
||||
// callback parameters: new value, from enter, dont't update panel UI (when called from edit field's kill focus handler for the PlusMinusButton)
|
||||
std::function<bool(coordf_t, bool, bool)> edit_fn = [](coordf_t, bool, bool) {return false; }
|
||||
);
|
||||
~LayerRangeEditor() {}
|
||||
|
||||
|
|
@ -69,8 +70,23 @@ public:
|
|||
ObjectLayers(wxWindow* parent);
|
||||
~ObjectLayers() {}
|
||||
|
||||
|
||||
// Button remembers the layer height range, for which it has been created.
|
||||
// The layer height range for this button is updated when the low or high boundary of the layer height range is updated
|
||||
// by the respective text edit field, so that this button emits an action for an up to date layer height range value.
|
||||
class PlusMinusButton : public ScalableButton
|
||||
{
|
||||
public:
|
||||
PlusMinusButton(wxWindow *parent, const ScalableBitmap &bitmap, std::pair<coordf_t, coordf_t> range) : ScalableButton(parent, wxID_ANY, bitmap), range(range) {}
|
||||
// updated when the text edit field loses focus for any PlusMinusButton.
|
||||
std::pair<coordf_t, coordf_t> range;
|
||||
};
|
||||
|
||||
void select_editor(LayerRangeEditor* editor, const bool is_last_edited_range);
|
||||
wxSizer* create_layer(const t_layer_height_range& range); // without_buttons
|
||||
// Create sizer with layer height range and layer height text edit fields, without buttons.
|
||||
// If the delete and add buttons are provided, the respective text edit fields will modify the layer height ranges of thes buttons
|
||||
// on value change, so that these buttons work with up to date values.
|
||||
wxSizer* create_layer(const t_layer_height_range& range, PlusMinusButton *delete_button, PlusMinusButton *add_button);
|
||||
void create_layers_list();
|
||||
void update_layers_list();
|
||||
|
||||
|
|
|
|||
|
|
@ -115,11 +115,23 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
|
||||
// describe control behavior
|
||||
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) {
|
||||
// detect the current mouse position here, to pass it to list_manipulation() method
|
||||
// if we detect it later, the user may have moved the mouse pointer while calculations are performed, and this would mess-up the HitTest() call performed into list_manipulation()
|
||||
// see: https://github.com/prusa3d/PrusaSlicer/issues/3802
|
||||
const wxPoint mouse_pos = this->get_mouse_position_in_control();
|
||||
|
||||
#ifndef __APPLE__
|
||||
// On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called
|
||||
// before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object
|
||||
// manipulator cache with the following call to selection_changed()
|
||||
wxGetApp().obj_manipul()->emulate_kill_focus();
|
||||
// wxGetApp().obj_manipul()->emulate_kill_focus(); // It's not necessury anymore #ys_FIXME delete after testing
|
||||
|
||||
// On Windows and Linux:
|
||||
// It's not invoked KillFocus event for "temporary" panels (like "Manipulation panel", "Settings", "Layer ranges"),
|
||||
// if we change selection in object list.
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/3303
|
||||
// But, if we call SetFocus() for ObjectList it will cause an invoking of a KillFocus event for "temporary" panels
|
||||
this->SetFocus();
|
||||
#else
|
||||
// To avoid selection update from SetSelection() and UnselectAll() under osx
|
||||
if (m_prevent_list_events)
|
||||
|
|
@ -150,7 +162,7 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
|
||||
wxDataViewItem item;
|
||||
wxDataViewColumn *col;
|
||||
this->HitTest(get_mouse_position_in_control(), item, col);
|
||||
this->HitTest(this->get_mouse_position_in_control(), item, col);
|
||||
new_selected_column = (col == nullptr) ? -1 : (int)col->GetModelColumn();
|
||||
if (new_selected_item == m_last_selected_item && m_last_selected_column != -1 && m_last_selected_column != new_selected_column) {
|
||||
// Mouse clicked on another column of the active row. Simulate keyboard enter to enter the editing mode of the current column.
|
||||
|
|
@ -166,11 +178,11 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
|
||||
selection_changed();
|
||||
#ifndef __WXMSW__
|
||||
set_tooltip_for_item(get_mouse_position_in_control());
|
||||
set_tooltip_for_item(this->get_mouse_position_in_control());
|
||||
#endif //__WXMSW__
|
||||
|
||||
#ifndef __WXOSX__
|
||||
list_manipulation();
|
||||
list_manipulation(mouse_pos);
|
||||
#endif //__WXOSX__
|
||||
});
|
||||
|
||||
|
|
@ -206,7 +218,7 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
|
||||
#ifdef __WXMSW__
|
||||
GetMainWindow()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
|
||||
set_tooltip_for_item(get_mouse_position_in_control());
|
||||
set_tooltip_for_item(this->get_mouse_position_in_control());
|
||||
event.Skip();
|
||||
});
|
||||
#endif //__WXMSW__
|
||||
|
|
@ -414,14 +426,6 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
|
|||
GetMainWindow()->SetToolTip(tooltip);
|
||||
}
|
||||
|
||||
wxPoint ObjectList::get_mouse_position_in_control()
|
||||
{
|
||||
const wxPoint& pt = wxGetMousePosition();
|
||||
// wxWindow* win = GetMainWindow();
|
||||
// wxPoint screen_pos = win->GetScreenPosition();
|
||||
return wxPoint(pt.x - /*win->*/GetScreenPosition().x, pt.y - /*win->*/GetScreenPosition().y);
|
||||
}
|
||||
|
||||
int ObjectList::get_selected_obj_idx() const
|
||||
{
|
||||
if (GetSelectedItemsCount() == 1)
|
||||
|
|
@ -783,23 +787,31 @@ void ObjectList::OnChar(wxKeyEvent& event)
|
|||
*/
|
||||
#endif /* __WXOSX__ */
|
||||
|
||||
void ObjectList::OnContextMenu(wxDataViewEvent&)
|
||||
void ObjectList::OnContextMenu(wxDataViewEvent& evt)
|
||||
{
|
||||
// The mouse position returned by get_mouse_position_in_control() here is the one at the time the mouse button is released (mouse up event)
|
||||
wxPoint mouse_pos = this->get_mouse_position_in_control();
|
||||
|
||||
// Do not show the context menu if the user pressed the right mouse button on the 3D scene and released it on the objects list
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
bool evt_context_menu = (canvas != nullptr) ? !canvas->is_mouse_dragging() : true;
|
||||
if (!evt_context_menu)
|
||||
canvas->mouse_up_cleanup();
|
||||
|
||||
list_manipulation(evt_context_menu);
|
||||
list_manipulation(mouse_pos, evt_context_menu);
|
||||
}
|
||||
|
||||
void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
|
||||
void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_menu/* = false*/)
|
||||
{
|
||||
// Interesting fact: when mouse_pos.x < 0, HitTest(mouse_pos, item, col) returns item = null, but column = last column.
|
||||
// So, when mouse was moved to scene immediately after clicking in ObjectList, in the scene will be shown context menu for the Editing column.
|
||||
// see: https://github.com/prusa3d/PrusaSlicer/issues/3802
|
||||
if (mouse_pos.x < 0)
|
||||
return;
|
||||
|
||||
wxDataViewItem item;
|
||||
wxDataViewColumn* col = nullptr;
|
||||
const wxPoint pt = get_mouse_position_in_control();
|
||||
HitTest(pt, item, col);
|
||||
HitTest(mouse_pos, item, col);
|
||||
|
||||
if (m_extruder_editor)
|
||||
m_extruder_editor->Hide();
|
||||
|
|
@ -836,30 +848,32 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
|
|||
Select(item);
|
||||
}
|
||||
|
||||
const wxString title = col->GetTitle();
|
||||
|
||||
if (title == " ")
|
||||
toggle_printable_state(item);
|
||||
else if (title == _("Editing"))
|
||||
show_context_menu(evt_context_menu);
|
||||
else if (title == _("Name"))
|
||||
if (col != nullptr)
|
||||
{
|
||||
if (wxOSX)
|
||||
show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909)
|
||||
const wxString title = col->GetTitle();
|
||||
if (title == " ")
|
||||
toggle_printable_state(item);
|
||||
else if (title == _("Editing"))
|
||||
show_context_menu(evt_context_menu);
|
||||
else if (title == _("Name"))
|
||||
{
|
||||
if (wxOSX)
|
||||
show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909)
|
||||
|
||||
if (is_windows10())
|
||||
{
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||
if (is_windows10())
|
||||
{
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||
|
||||
if (get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
|
||||
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
|
||||
fix_through_netfabb();
|
||||
}
|
||||
}
|
||||
// workaround for extruder editing under OSX
|
||||
else if (wxOSX && evt_context_menu && title == _("Extruder"))
|
||||
extruder_editing();
|
||||
if (get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
|
||||
mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit())
|
||||
fix_through_netfabb();
|
||||
}
|
||||
}
|
||||
// workaround for extruder editing under OSX
|
||||
else if (wxOSX && evt_context_menu && title == _("Extruder"))
|
||||
extruder_editing();
|
||||
}
|
||||
|
||||
#ifndef __WXMSW__
|
||||
GetMainWindow()->SetToolTip(""); // hide tooltip
|
||||
|
|
@ -909,7 +923,7 @@ void ObjectList::extruder_editing()
|
|||
|
||||
const int column_width = GetColumn(colExtruder)->GetWidth() + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 5;
|
||||
|
||||
wxPoint pos = get_mouse_position_in_control();
|
||||
wxPoint pos = this->get_mouse_position_in_control();
|
||||
wxSize size = wxSize(column_width, -1);
|
||||
pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5;
|
||||
pos.y -= GetTextExtent("m").y;
|
||||
|
|
@ -2864,13 +2878,13 @@ void ObjectList::del_layer_range(const t_layer_height_range& range)
|
|||
static double get_min_layer_height(const int extruder_idx)
|
||||
{
|
||||
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
return config.opt_float("min_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
|
||||
return config.opt_float("min_layer_height", std::max(0, extruder_idx - 1));
|
||||
}
|
||||
|
||||
static double get_max_layer_height(const int extruder_idx)
|
||||
{
|
||||
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
int extruder_idx_zero_based = extruder_idx <= 0 ? 0 : extruder_idx-1;
|
||||
int extruder_idx_zero_based = std::max(0, extruder_idx - 1);
|
||||
double max_layer_height = config.opt_float("max_layer_height", extruder_idx_zero_based);
|
||||
|
||||
// In case max_layer_height is set to zero, it should default to 75 % of nozzle diameter:
|
||||
|
|
@ -2880,80 +2894,139 @@ static double get_max_layer_height(const int extruder_idx)
|
|||
return max_layer_height;
|
||||
}
|
||||
|
||||
void ObjectList::add_layer_range_after_current(const t_layer_height_range& current_range)
|
||||
// When editing this function, please synchronize the conditions with can_add_new_range_after_current().
|
||||
void ObjectList::add_layer_range_after_current(const t_layer_height_range current_range)
|
||||
{
|
||||
const int obj_idx = get_selected_obj_idx();
|
||||
if (obj_idx < 0) return;
|
||||
assert(obj_idx >= 0);
|
||||
if (obj_idx < 0)
|
||||
// This should not happen.
|
||||
return;
|
||||
|
||||
const wxDataViewItem layers_item = GetSelection();
|
||||
|
||||
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||
auto it_range = ranges.find(current_range);
|
||||
assert(it_range != ranges.end());
|
||||
if (it_range == ranges.end())
|
||||
// This shoudl not happen.
|
||||
return;
|
||||
|
||||
const t_layer_height_range& last_range = (--ranges.end())->first;
|
||||
|
||||
if (current_range == last_range)
|
||||
auto it_next_range = it_range;
|
||||
bool changed = false;
|
||||
if (++ it_next_range == ranges.end())
|
||||
{
|
||||
// Adding a new layer height range after the last one.
|
||||
take_snapshot(_(L("Add Height Range")));
|
||||
changed = true;
|
||||
|
||||
const t_layer_height_range& new_range = { last_range.second, last_range.second + 2. };
|
||||
const t_layer_height_range new_range = { current_range.second, current_range.second + 2. };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item);
|
||||
}
|
||||
else
|
||||
else if (const std::pair<coordf_t, coordf_t> &next_range = it_next_range->first; current_range.second <= next_range.first)
|
||||
{
|
||||
const t_layer_height_range& next_range = (++ranges.find(current_range))->first;
|
||||
|
||||
if (current_range.second > next_range.first)
|
||||
return; // range division has no sense
|
||||
|
||||
const int layer_idx = m_objects_model->GetItemIdByLayerRange(obj_idx, next_range);
|
||||
if (layer_idx < 0)
|
||||
return;
|
||||
|
||||
if (current_range.second == next_range.first)
|
||||
assert(layer_idx >= 0);
|
||||
if (layer_idx >= 0)
|
||||
{
|
||||
const auto old_config = ranges.at(next_range);
|
||||
if (current_range.second == next_range.first)
|
||||
{
|
||||
// Splitting the next layer height range to two.
|
||||
const auto old_config = ranges.at(next_range);
|
||||
const coordf_t delta = next_range.second - next_range.first;
|
||||
// Layer height of the current layer.
|
||||
const coordf_t old_min_layer_height = get_min_layer_height(old_config.opt_int("extruder"));
|
||||
// Layer height of the layer to be inserted.
|
||||
const coordf_t new_min_layer_height = get_min_layer_height(0);
|
||||
if (delta >= old_min_layer_height + new_min_layer_height - EPSILON) {
|
||||
const coordf_t middle_layer_z = (new_min_layer_height > 0.5 * delta) ?
|
||||
next_range.second - new_min_layer_height :
|
||||
next_range.first + std::max(old_min_layer_height, 0.5 * delta);
|
||||
t_layer_height_range new_range = { middle_layer_z, next_range.second };
|
||||
|
||||
const coordf_t delta = (next_range.second - next_range.first);
|
||||
if (delta < get_min_layer_height(old_config.opt_int("extruder"))/*0.05f*/) // next range division has no sense
|
||||
return;
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add Height Range")));
|
||||
changed = true;
|
||||
|
||||
const coordf_t midl_layer = next_range.first + 0.5 * delta;
|
||||
|
||||
t_layer_height_range new_range = { midl_layer, next_range.second };
|
||||
// create new 2 layers instead of deleted one
|
||||
// delete old layer
|
||||
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add Height Range")));
|
||||
wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, next_range);
|
||||
del_subobject_item(layer_item);
|
||||
|
||||
// create new 2 layers instead of deleted one
|
||||
ranges[new_range] = old_config;
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
|
||||
// delete old layer
|
||||
new_range = { current_range.second, middle_layer_z };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
}
|
||||
}
|
||||
else if (next_range.first - current_range.second >= get_min_layer_height(0) - EPSILON)
|
||||
{
|
||||
// Filling in a gap between the current and a new layer height range with a new one.
|
||||
take_snapshot(_(L("Add Height Range")));
|
||||
changed = true;
|
||||
|
||||
wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, next_range);
|
||||
del_subobject_item(layer_item);
|
||||
|
||||
ranges[new_range] = old_config;
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
|
||||
new_range = { current_range.second, midl_layer };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
const t_layer_height_range new_range = { current_range.second, next_range.first };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
take_snapshot(_(L("Add Height Range")));
|
||||
|
||||
const t_layer_height_range new_range = { current_range.second, next_range.first };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
}
|
||||
}
|
||||
|
||||
changed_object(obj_idx);
|
||||
if (changed)
|
||||
changed_object(obj_idx);
|
||||
|
||||
// The layer range panel is updated even if this function does not change the layer ranges, as the panel update
|
||||
// may have been postponed from the "kill focus" event of a text field, if the focus was lost for the "add layer" button.
|
||||
// select item to update layers sizer
|
||||
select_item(layers_item);
|
||||
}
|
||||
|
||||
// Returning an empty string means that the layer could be added after the current layer.
|
||||
// Otherwise an error tooltip is returned.
|
||||
// When editing this function, please synchronize the conditions with add_layer_range_after_current().
|
||||
wxString ObjectList::can_add_new_range_after_current(const t_layer_height_range current_range)
|
||||
{
|
||||
const int obj_idx = get_selected_obj_idx();
|
||||
assert(obj_idx >= 0);
|
||||
if (obj_idx < 0)
|
||||
// This should not happen.
|
||||
return "ObjectList assert";
|
||||
|
||||
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||
auto it_range = ranges.find(current_range);
|
||||
assert(it_range != ranges.end());
|
||||
if (it_range == ranges.end())
|
||||
// This shoudl not happen.
|
||||
return "ObjectList assert";
|
||||
|
||||
auto it_next_range = it_range;
|
||||
if (++ it_next_range == ranges.end())
|
||||
// Adding a layer after the last layer is always possible.
|
||||
return "";
|
||||
|
||||
if (const std::pair<coordf_t, coordf_t>& next_range = it_next_range->first; current_range.second <= next_range.first)
|
||||
{
|
||||
if (current_range.second == next_range.first) {
|
||||
if (next_range.second - next_range.first < get_min_layer_height(it_next_range->second.opt_int("extruder")) + get_min_layer_height(0) - EPSILON)
|
||||
return _(L("Cannot insert a new layer range after the current layer range.\n"
|
||||
"The next layer range is too thin to be split to two\n"
|
||||
"without violating the minimum layer height."));
|
||||
} else if (next_range.first - current_range.second < get_min_layer_height(0) - EPSILON) {
|
||||
return _(L("Cannot insert a new layer range between the current and the next layer range.\n"
|
||||
"The gap between the current layer range and the next layer range\n"
|
||||
"is thinner than the minimum layer height allowed."));
|
||||
}
|
||||
} else
|
||||
return _(L("Cannot insert a new layer range after the current layer range.\n"
|
||||
"Current layer range overlaps with the next layer range."));
|
||||
|
||||
// All right, new layer height range could be inserted.
|
||||
return "";
|
||||
}
|
||||
|
||||
void ObjectList::add_layer_item(const t_layer_height_range& range,
|
||||
const wxDataViewItem layers_item,
|
||||
const int layer_idx /* = -1*/)
|
||||
|
|
@ -2974,7 +3047,10 @@ void ObjectList::add_layer_item(const t_layer_height_range& range,
|
|||
|
||||
bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t layer_height)
|
||||
{
|
||||
const int obj_idx = get_selected_obj_idx();
|
||||
// Use m_selected_object_id instead of get_selected_obj_idx()
|
||||
// because of get_selected_obj_idx() return obj_idx for currently selected item.
|
||||
// But edit_layer_range(...) function can be called, when Selection in ObjectList could be changed
|
||||
const int obj_idx = m_selected_object_id ;
|
||||
if (obj_idx < 0)
|
||||
return false;
|
||||
|
||||
|
|
@ -2995,9 +3071,12 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t la
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_layer_height_range& new_range)
|
||||
bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_layer_height_range& new_range, bool dont_update_ui)
|
||||
{
|
||||
const int obj_idx = get_selected_obj_idx();
|
||||
// Use m_selected_object_id instead of get_selected_obj_idx()
|
||||
// because of get_selected_obj_idx() return obj_idx for currently selected item.
|
||||
// But edit_layer_range(...) function can be called, when Selection in ObjectList could be changed
|
||||
const int obj_idx = m_selected_object_id;
|
||||
if (obj_idx < 0) return false;
|
||||
|
||||
take_snapshot(_(L("Edit Height Range")));
|
||||
|
|
@ -3011,21 +3090,26 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
|
|||
ranges.erase(range);
|
||||
ranges[new_range] = config;
|
||||
changed_object(obj_idx);
|
||||
|
||||
|
||||
wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
|
||||
// To avoid update selection after deleting of a selected item (under GTK)
|
||||
// set m_prevent_list_events to true
|
||||
m_prevent_list_events = true;
|
||||
m_objects_model->DeleteChildren(root_item);
|
||||
|
||||
if (root_item.IsOk())
|
||||
if (root_item.IsOk()) {
|
||||
// create Layer item(s) according to the layer_config_ranges
|
||||
for (const auto& r : ranges)
|
||||
add_layer_item(r.first, root_item);
|
||||
}
|
||||
|
||||
// if this function was invoked from wxEVT_CHANGE_SELECTION selected item could be other than itLayer or itLayerRoot
|
||||
if (!dont_update_ui && (sel_type & (itLayer | itLayerRoot)))
|
||||
select_item(sel_type&itLayer ? m_objects_model->GetItemByLayerRange(obj_idx, new_range) : root_item);
|
||||
|
||||
select_item(sel_type&itLayer ? m_objects_model->GetItemByLayerRange(obj_idx, new_range) : root_item);
|
||||
Expand(root_item);
|
||||
|
||||
m_prevent_list_events = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ public:
|
|||
bool selected_instances_of_same_object();
|
||||
bool can_split_instances();
|
||||
|
||||
wxPoint get_mouse_position_in_control();
|
||||
wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); }
|
||||
wxBoxSizer* get_sizer() {return m_sizer;}
|
||||
int get_selected_obj_idx() const;
|
||||
DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const;
|
||||
|
|
@ -320,13 +320,27 @@ public:
|
|||
// Remove objects/sub-object from the list
|
||||
void remove();
|
||||
void del_layer_range(const t_layer_height_range& range);
|
||||
void add_layer_range_after_current(const t_layer_height_range& current_range);
|
||||
// Add a new layer height after the current range if possible.
|
||||
// The current range is shortened and the new range is entered after the shortened current range if it fits.
|
||||
// If no range fits after the current range, then no range is inserted.
|
||||
// The layer range panel is updated even if this function does not change the layer ranges, as the panel update
|
||||
// may have been postponed from the "kill focus" event of a text field, if the focus was lost for the "add layer" button.
|
||||
// Rather providing the range by a value than by a reference, so that the memory referenced cannot be invalidated.
|
||||
void add_layer_range_after_current(const t_layer_height_range current_range);
|
||||
wxString can_add_new_range_after_current( t_layer_height_range current_range);
|
||||
void add_layer_item (const t_layer_height_range& range,
|
||||
const wxDataViewItem layers_item,
|
||||
const int layer_idx = -1);
|
||||
bool edit_layer_range(const t_layer_height_range& range, coordf_t layer_height);
|
||||
// This function may be called when a text field loses focus for a "add layer" or "remove layer" button.
|
||||
// In that case we don't want to destroy the panel with that "add layer" or "remove layer" buttons, as some messages
|
||||
// are already planned for them and destroying these widgets leads to crashes at least on OSX.
|
||||
// In that case the "add layer" or "remove layer" button handlers are responsible for always rebuilding the panel
|
||||
// even if the "add layer" or "remove layer" buttons did not update the layer spans or layer heights.
|
||||
bool edit_layer_range(const t_layer_height_range& range,
|
||||
const t_layer_height_range& new_range);
|
||||
const t_layer_height_range& new_range,
|
||||
// Don't destroy the panel with the "add layer" or "remove layer" buttons.
|
||||
bool suppress_ui_update = false);
|
||||
|
||||
void init_objects();
|
||||
bool multiple_selection() const ;
|
||||
|
|
@ -381,7 +395,7 @@ private:
|
|||
// void OnChar(wxKeyEvent& event);
|
||||
#endif /* __WXOSX__ */
|
||||
void OnContextMenu(wxDataViewEvent &event);
|
||||
void list_manipulation(bool evt_context_menu = false);
|
||||
void list_manipulation(const wxPoint& mouse_pos, bool evt_context_menu = false);
|
||||
|
||||
void OnBeginDrag(wxDataViewEvent &event);
|
||||
void OnDropPossible(wxDataViewEvent &event);
|
||||
|
|
|
|||
|
|
@ -27,31 +27,63 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
: m_canvas_widget(nullptr)
|
||||
, m_canvas(nullptr)
|
||||
{
|
||||
init(parent, model, config, process);
|
||||
}
|
||||
#else
|
||||
View3D::View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
: m_canvas_widget(nullptr)
|
||||
, m_canvas(nullptr)
|
||||
{
|
||||
init(parent, bed, camera, view_toolbar, model, config, process);
|
||||
}
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
View3D::~View3D()
|
||||
{
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
if (m_canvas != nullptr)
|
||||
delete m_canvas;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
if (m_canvas_widget != nullptr)
|
||||
{
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
_3DScene::remove_canvas(m_canvas_widget);
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
delete m_canvas_widget;
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
m_canvas = nullptr;
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
#else
|
||||
bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
{
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(*this);
|
||||
if (m_canvas_widget == nullptr)
|
||||
return false;
|
||||
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget);
|
||||
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
|
||||
m_canvas->bind_event_handlers();
|
||||
#else
|
||||
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this);
|
||||
_3DScene::add_canvas(m_canvas_widget, bed, camera, view_toolbar);
|
||||
m_canvas = _3DScene::get_canvas(this->m_canvas_widget);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
m_canvas->allow_multisample(GLCanvas3DManager::can_multisample());
|
||||
// XXX: If have OpenGL
|
||||
|
|
@ -66,6 +98,9 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_
|
|||
m_canvas->enable_main_toolbar(true);
|
||||
m_canvas->enable_undoredo_toolbar(true);
|
||||
m_canvas->enable_labels(true);
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
m_canvas->enable_slope(true);
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
|
||||
|
|
@ -163,9 +198,15 @@ void View3D::render()
|
|||
m_canvas->set_as_dirty();
|
||||
}
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
Preview::Preview(
|
||||
wxWindow* parent, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process_func)
|
||||
#else
|
||||
Preview::Preview(
|
||||
wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process_func)
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
: m_canvas_widget(nullptr)
|
||||
, m_canvas(nullptr)
|
||||
, m_double_slider_sizer(nullptr)
|
||||
|
|
@ -190,21 +231,39 @@ Preview::Preview(
|
|||
, m_volumes_cleanup_required(false)
|
||||
#endif // __linux__
|
||||
{
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
if (init(parent, model))
|
||||
#else
|
||||
if (init(parent, bed, camera, view_toolbar, model))
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
{
|
||||
show_hide_ui_elements("none");
|
||||
load_print();
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool Preview::init(wxWindow* parent, Model* model)
|
||||
#else
|
||||
bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model)
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
{
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(*this);
|
||||
if (m_canvas_widget == nullptr)
|
||||
return false;
|
||||
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget);
|
||||
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
|
||||
m_canvas->bind_event_handlers();
|
||||
#else
|
||||
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this);
|
||||
_3DScene::add_canvas(m_canvas_widget, bed, camera, view_toolbar);
|
||||
m_canvas = _3DScene::get_canvas(this->m_canvas_widget);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
m_canvas->allow_multisample(GLCanvas3DManager::can_multisample());
|
||||
m_canvas->set_config(m_config);
|
||||
m_canvas->set_model(model);
|
||||
|
|
@ -313,9 +372,16 @@ Preview::~Preview()
|
|||
{
|
||||
unbind_event_handlers();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
if (m_canvas != nullptr)
|
||||
delete m_canvas;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
if (m_canvas_widget != nullptr)
|
||||
{
|
||||
_3DScene::remove_canvas(m_canvas_widget);
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
_3DScene::remove_canvas(m_canvas_widget);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
delete m_canvas_widget;
|
||||
m_canvas = nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ class GLCanvas3D;
|
|||
class GLToolbar;
|
||||
class Bed3D;
|
||||
struct Camera;
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
class Plater;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
class View3D : public wxPanel
|
||||
{
|
||||
|
|
@ -41,7 +44,11 @@ class View3D : public wxPanel
|
|||
GLCanvas3D* m_canvas;
|
||||
|
||||
public:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
#else
|
||||
View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
virtual ~View3D();
|
||||
|
||||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
|
|
@ -69,7 +76,11 @@ public:
|
|||
void render();
|
||||
|
||||
private:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
#else
|
||||
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
};
|
||||
|
||||
class Preview : public wxPanel
|
||||
|
|
@ -109,8 +120,13 @@ class Preview : public wxPanel
|
|||
DoubleSlider::Control* m_slider {nullptr};
|
||||
|
||||
public:
|
||||
Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process = []() {});
|
||||
#else
|
||||
Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process = [](){});
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
virtual ~Preview();
|
||||
|
||||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
|
|
@ -137,7 +153,11 @@ public:
|
|||
bool is_loaded() const { return m_loaded; }
|
||||
|
||||
private:
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
bool init(wxWindow* parent, Model* model);
|
||||
#else
|
||||
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
void bind_event_handlers();
|
||||
void unbind_event_handlers();
|
||||
|
|
@ -170,7 +190,6 @@ private:
|
|||
void load_print_as_sla();
|
||||
|
||||
void on_sliders_scroll_changed(wxCommandEvent& event);
|
||||
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#ifdef _WIN32
|
||||
wxDEFINE_EVENT(EVT_HID_DEVICE_ATTACHED, HIDDeviceAttachedEvent);
|
||||
wxDEFINE_EVENT(EVT_HID_DEVICE_DETACHED, HIDDeviceDetachedEvent);
|
||||
wxDEFINE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
|
||||
wxDEFINE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
|
||||
#endif // _WIN32
|
||||
|
||||
wxTopLevelWindow* find_toplevel_parent(wxWindow *window)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#include <wx/debug.h>
|
||||
#include <wx/settings.h>
|
||||
|
||||
#include "Event.hpp"
|
||||
|
||||
class wxCheckBox;
|
||||
class wxTopLevelWindow;
|
||||
class wxRect;
|
||||
|
|
@ -26,6 +28,19 @@ class wxRect;
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#ifdef _WIN32
|
||||
// USB HID attach / detach events from Windows OS.
|
||||
using HIDDeviceAttachedEvent = Event<std::string>;
|
||||
using HIDDeviceDetachedEvent = Event<std::string>;
|
||||
wxDECLARE_EVENT(EVT_HID_DEVICE_ATTACHED, HIDDeviceAttachedEvent);
|
||||
wxDECLARE_EVENT(EVT_HID_DEVICE_DETACHED, HIDDeviceDetachedEvent);
|
||||
|
||||
// Disk aka Volume attach / detach events from Windows OS.
|
||||
using VolumeAttachedEvent = SimpleEvent;
|
||||
using VolumeDetachedEvent = SimpleEvent;
|
||||
wxDECLARE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
|
||||
wxDECLARE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
|
||||
#endif /* _WIN32 */
|
||||
|
||||
wxTopLevelWindow* find_toplevel_parent(wxWindow *window);
|
||||
|
||||
|
|
|
|||
|
|
@ -262,12 +262,6 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoBase::set_tooltip(const std::string& tooltip) const
|
||||
{
|
||||
m_parent.set_tooltip(tooltip);
|
||||
}
|
||||
|
||||
std::string GLGizmoBase::format(float value, unsigned int decimals) const
|
||||
{
|
||||
return Slic3r::string_printf("%.*f", decimals, value);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
|
|||
class ImGuiWrapper;
|
||||
class GLCanvas3D;
|
||||
class ClippingPlane;
|
||||
enum class CommonGizmosDataID;
|
||||
class CommonGizmosDataPool;
|
||||
|
||||
class GLGizmoBase
|
||||
{
|
||||
|
|
@ -100,6 +102,8 @@ protected:
|
|||
mutable std::vector<Grabber> m_grabbers;
|
||||
ImGuiWrapper* m_imgui;
|
||||
bool m_first_input_window_render;
|
||||
mutable std::string m_tooltip;
|
||||
CommonGizmosDataPool* m_c;
|
||||
|
||||
public:
|
||||
GLGizmoBase(GLCanvas3D& parent,
|
||||
|
|
@ -127,6 +131,8 @@ public:
|
|||
|
||||
bool is_activable() const { return on_is_activable(); }
|
||||
bool is_selectable() const { return on_is_selectable(); }
|
||||
CommonGizmosDataID get_requirements() const { return on_get_requirements(); }
|
||||
void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; }
|
||||
|
||||
unsigned int get_sprite_id() const { return m_sprite_id; }
|
||||
|
||||
|
|
@ -145,10 +151,12 @@ public:
|
|||
|
||||
void update(const UpdateData& data);
|
||||
|
||||
void render() const { on_render(); }
|
||||
void render() const { m_tooltip.clear(); on_render(); }
|
||||
void render_for_picking() const { on_render_for_picking(); }
|
||||
void render_input_window(float x, float y, float bottom_limit);
|
||||
|
||||
virtual std::string get_tooltip() const { return ""; }
|
||||
|
||||
protected:
|
||||
virtual bool on_init() = 0;
|
||||
virtual void on_load(cereal::BinaryInputArchive& ar) {}
|
||||
|
|
@ -158,6 +166,7 @@ protected:
|
|||
virtual void on_set_hover_id() {}
|
||||
virtual bool on_is_activable() const { return true; }
|
||||
virtual bool on_is_selectable() const { return true; }
|
||||
virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); }
|
||||
virtual void on_enable_grabber(unsigned int id) {}
|
||||
virtual void on_disable_grabber(unsigned int id) {}
|
||||
virtual void on_start_dragging() {}
|
||||
|
|
@ -174,7 +183,6 @@ protected:
|
|||
void render_grabbers(float size) const;
|
||||
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
|
||||
|
||||
void set_tooltip(const std::string& tooltip) const;
|
||||
std::string format(float value, unsigned int decimals) const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, uns
|
|||
, m_rotate_lower(false)
|
||||
{}
|
||||
|
||||
std::string GLGizmoCut::get_tooltip() const
|
||||
{
|
||||
return (m_hover_id == 0 || m_grabbers[0].dragging) ? "Z: " + format(m_cut_z, 2) : "";
|
||||
}
|
||||
|
||||
bool GLGizmoCut::on_init()
|
||||
{
|
||||
m_grabbers.emplace_back();
|
||||
|
|
@ -79,10 +84,6 @@ void GLGizmoCut::on_update(const UpdateData& data)
|
|||
|
||||
void GLGizmoCut::on_render() const
|
||||
{
|
||||
if (m_grabbers[0].dragging) {
|
||||
set_tooltip("Z: " + format(m_cut_z, 2));
|
||||
}
|
||||
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
|
||||
update_max_z(selection);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ public:
|
|||
double get_cut_z() const { return m_cut_z; }
|
||||
void set_cut_z(double cut_z) const;
|
||||
|
||||
std::string get_tooltip() const override;
|
||||
|
||||
protected:
|
||||
virtual bool on_init();
|
||||
virtual void on_load(cereal::BinaryInputArchive& ar) { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "GLGizmoFlatten.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
|
|
@ -26,20 +27,15 @@ bool GLGizmoFlatten::on_init()
|
|||
|
||||
void GLGizmoFlatten::on_set_state()
|
||||
{
|
||||
// m_model_object pointer can be invalid (for instance because of undo/redo action),
|
||||
// we should recover it from the object id
|
||||
m_model_object = nullptr;
|
||||
for (const auto mo : wxGetApp().model().objects) {
|
||||
if (mo->id() == m_model_object_id) {
|
||||
m_model_object = mo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state == On && is_plane_update_necessary())
|
||||
update_planes();
|
||||
}
|
||||
|
||||
CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const
|
||||
{
|
||||
return CommonGizmosDataID::SelectionInfo;
|
||||
}
|
||||
|
||||
std::string GLGizmoFlatten::on_get_name() const
|
||||
{
|
||||
return (_(L("Place on face")) + " [F]").ToUTF8().data();
|
||||
|
|
@ -132,18 +128,17 @@ void GLGizmoFlatten::on_render_for_picking() const
|
|||
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
|
||||
{
|
||||
m_starting_center = Vec3d::Zero();
|
||||
if (m_model_object != model_object) {
|
||||
if (model_object != m_old_model_object) {
|
||||
m_planes.clear();
|
||||
m_planes_valid = false;
|
||||
}
|
||||
m_model_object = model_object;
|
||||
m_model_object_id = model_object ? model_object->id() : 0;
|
||||
}
|
||||
|
||||
void GLGizmoFlatten::update_planes()
|
||||
{
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
TriangleMesh ch;
|
||||
for (const ModelVolume* vol : m_model_object->volumes)
|
||||
for (const ModelVolume* vol : mo->volumes)
|
||||
{
|
||||
if (vol->type() != ModelVolumeType::MODEL_PART)
|
||||
continue;
|
||||
|
|
@ -153,7 +148,7 @@ void GLGizmoFlatten::update_planes()
|
|||
}
|
||||
ch = ch.convex_hull_3d();
|
||||
m_planes.clear();
|
||||
const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix(true);
|
||||
const Transform3d& inst_matrix = mo->instances.front()->get_matrix(true);
|
||||
|
||||
// Following constants are used for discarding too small polygons.
|
||||
const float minimal_area = 5.f; // in square mm (world coordinates)
|
||||
|
|
@ -331,12 +326,13 @@ void GLGizmoFlatten::update_planes()
|
|||
// Planes are finished - let's save what we calculated it from:
|
||||
m_volumes_matrices.clear();
|
||||
m_volumes_types.clear();
|
||||
for (const ModelVolume* vol : m_model_object->volumes) {
|
||||
for (const ModelVolume* vol : mo->volumes) {
|
||||
m_volumes_matrices.push_back(vol->get_matrix());
|
||||
m_volumes_types.push_back(vol->type());
|
||||
}
|
||||
m_first_instance_scale = m_model_object->instances.front()->get_scaling_factor();
|
||||
m_first_instance_mirror = m_model_object->instances.front()->get_mirror();
|
||||
m_first_instance_scale = mo->instances.front()->get_scaling_factor();
|
||||
m_first_instance_mirror = mo->instances.front()->get_mirror();
|
||||
m_old_model_object = mo;
|
||||
|
||||
m_planes_valid = true;
|
||||
}
|
||||
|
|
@ -344,20 +340,22 @@ void GLGizmoFlatten::update_planes()
|
|||
|
||||
bool GLGizmoFlatten::is_plane_update_necessary() const
|
||||
{
|
||||
if (m_state != On || !m_model_object || m_model_object->instances.empty())
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
if (m_state != On || ! mo || mo->instances.empty())
|
||||
return false;
|
||||
|
||||
if (! m_planes_valid || m_model_object->volumes.size() != m_volumes_matrices.size())
|
||||
if (! m_planes_valid || mo != m_old_model_object
|
||||
|| mo->volumes.size() != m_volumes_matrices.size())
|
||||
return true;
|
||||
|
||||
// We want to recalculate when the scale changes - some planes could (dis)appear.
|
||||
if (! m_model_object->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale)
|
||||
|| ! m_model_object->instances.front()->get_mirror().isApprox(m_first_instance_mirror))
|
||||
if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale)
|
||||
|| ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror))
|
||||
return true;
|
||||
|
||||
for (unsigned int i=0; i < m_model_object->volumes.size(); ++i)
|
||||
if (! m_model_object->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i])
|
||||
|| m_model_object->volumes[i]->type() != m_volumes_types[i])
|
||||
for (unsigned int i=0; i < mo->volumes.size(); ++i)
|
||||
if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i])
|
||||
|| mo->volumes[i]->type() != m_volumes_types[i])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ private:
|
|||
std::vector<PlaneData> m_planes;
|
||||
bool m_planes_valid = false;
|
||||
mutable Vec3d m_starting_center;
|
||||
const ModelObject* m_model_object = nullptr;
|
||||
ObjectID m_model_object_id = 0;
|
||||
const ModelObject* m_old_model_object = nullptr;
|
||||
std::vector<const Transform3d*> instances_matrices;
|
||||
|
||||
void update_planes();
|
||||
|
|
@ -51,6 +50,7 @@ protected:
|
|||
virtual void on_render() const override;
|
||||
virtual void on_render_for_picking() const override;
|
||||
virtual void on_set_state() override;
|
||||
virtual CommonGizmosDataID on_get_requirements() const override;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
#include "GLGizmoHollow.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectSettings.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/MeshUtils.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/PresetBundle.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -56,32 +53,14 @@ bool GLGizmoHollow::on_init()
|
|||
|
||||
void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
|
||||
{
|
||||
if (m_c->recent_update) {
|
||||
if (! m_c->selection_info())
|
||||
return;
|
||||
|
||||
if (m_state == On)
|
||||
m_c->build_AABB_if_needed();
|
||||
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
|
||||
// This is a temporary and not very nice hack, to make sure that
|
||||
// if the cp was moved by the data returned by backend, it will
|
||||
// remember its direction. FIXME: Refactor this mess and make
|
||||
// the clipping plane itself part of the shared data.
|
||||
if (! m_c->m_clipping_plane_was_moved && m_c->m_clipping_plane_distance == 0.25f)
|
||||
m_c->m_clipping_plane_was_moved = true;
|
||||
|
||||
|
||||
if (m_c->m_model_object) {
|
||||
reload_cache();
|
||||
if (m_c->has_drilled_mesh())
|
||||
m_holes_in_drilled_mesh = m_c->m_model_object->sla_drain_holes;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state == On) {
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
|
||||
m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance);
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
if (mo) {
|
||||
reload_cache();
|
||||
if (m_c->hollowed_mesh()->get_hollowed_mesh())
|
||||
m_holes_in_drilled_mesh = mo->sla_drain_holes;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,12 +69,12 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
|
|||
void GLGizmoHollow::on_render() const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info();
|
||||
|
||||
// If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off
|
||||
if (m_state == On
|
||||
&& (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()]
|
||||
|| m_c->m_active_instance != selection.get_instance_idx()
|
||||
|| m_c->m_model_object_id != m_c->m_model_object->id())) {
|
||||
&& (sel_info->model_object() != selection.get_model()->objects[selection.get_object_idx()]
|
||||
|| sel_info->get_active_instance() != selection.get_instance_idx())) {
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS));
|
||||
return;
|
||||
}
|
||||
|
|
@ -103,92 +82,17 @@ void GLGizmoHollow::on_render() const
|
|||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
|
||||
|
||||
if (m_quadric != nullptr && selection.is_from_single_instance())
|
||||
render_points(selection, false);
|
||||
|
||||
m_selection_rectangle.render(m_parent);
|
||||
render_clipping_plane(selection);
|
||||
m_c->object_clipper()->render_cut();
|
||||
m_c->supports_clipper()->render_cut();
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoHollow::render_clipping_plane(const Selection& selection) const
|
||||
{
|
||||
if (m_c->m_clipping_plane_distance == 0.f)
|
||||
return;
|
||||
|
||||
// Get transformation of the instance
|
||||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Geometry::Transformation trafo = vol->get_instance_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
|
||||
// Get transformation of supports
|
||||
Geometry::Transformation supports_trafo;
|
||||
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z()));
|
||||
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
|
||||
// I don't know why, but following seems to be correct.
|
||||
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
|
||||
1,
|
||||
1.));
|
||||
|
||||
// Now initialize the TMS for the object, perform the cut and save the result.
|
||||
if (! m_c->m_object_clipper) {
|
||||
m_c->m_object_clipper.reset(new MeshClipper);
|
||||
m_c->m_object_clipper->set_mesh(*m_c->mesh());
|
||||
}
|
||||
m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane);
|
||||
m_c->m_object_clipper->set_transformation(trafo);
|
||||
|
||||
if (m_c->m_print_object_idx >= 0) {
|
||||
const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx];
|
||||
|
||||
if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) {
|
||||
// If the supports are already calculated, save the timestamp of the respective step
|
||||
// so we can later tell they were recalculated.
|
||||
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
|
||||
|
||||
if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) {
|
||||
// The timestamp has changed.
|
||||
m_c->m_supports_clipper.reset(new MeshClipper);
|
||||
// The mesh should already have the shared vertices calculated.
|
||||
m_c->m_supports_clipper->set_mesh(print_object->support_mesh());
|
||||
m_c->m_old_timestamp = timestamp;
|
||||
}
|
||||
m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane);
|
||||
m_c->m_supports_clipper->set_transformation(supports_trafo);
|
||||
}
|
||||
else
|
||||
// The supports are not valid. We better dump the cached data.
|
||||
m_c->m_supports_clipper.reset();
|
||||
}
|
||||
|
||||
// At this point we have the triangulated cuts for both the object and supports - let's render.
|
||||
if (! m_c->m_object_clipper->get_triangles().empty()) {
|
||||
::glPushMatrix();
|
||||
::glColor3f(1.0f, 0.37f, 0.0f);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
for (const Vec3f& point : m_c->m_object_clipper->get_triangles())
|
||||
::glVertex3f(point(0), point(1), point(2));
|
||||
::glEnd();
|
||||
::glPopMatrix();
|
||||
}
|
||||
|
||||
if (m_show_supports && m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty()) {
|
||||
::glPushMatrix();
|
||||
::glColor3f(1.0f, 0.f, 0.37f);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
for (const Vec3f& point : m_c->m_supports_clipper->get_triangles())
|
||||
::glVertex3f(point(0), point(1), point(2));
|
||||
::glEnd();
|
||||
::glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoHollow::on_render_for_picking() const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
|
|
@ -210,17 +114,18 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
|
|||
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(0.0, 0.0, m_z_shift));
|
||||
glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift()));
|
||||
glsafe(::glMultMatrixd(instance_matrix.data()));
|
||||
|
||||
float render_color[4];
|
||||
size_t cache_size = m_c->m_model_object->sla_drain_holes.size();
|
||||
const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
|
||||
size_t cache_size = drain_holes.size();
|
||||
for (size_t i = 0; i < cache_size; ++i)
|
||||
{
|
||||
const sla::DrainHole& drain_hole = m_c->m_model_object->sla_drain_holes[i];
|
||||
const sla::DrainHole& drain_hole = drain_holes[i];
|
||||
const bool& point_selected = m_selected[i];
|
||||
|
||||
if (is_mesh_point_clipped((drain_hole.pos+m_c->HoleStickOutLength*drain_hole.normal).cast<double>()))
|
||||
if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast<double>()))
|
||||
continue;
|
||||
|
||||
// First decide about the color of the point.
|
||||
|
|
@ -258,7 +163,6 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
|
|||
glFrontFace(GL_CW);
|
||||
|
||||
// Matrices set, we can render the point mark now.
|
||||
|
||||
Eigen::Quaterniond q;
|
||||
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast<double>());
|
||||
Eigen::AngleAxisd aa(q);
|
||||
|
|
@ -294,12 +198,17 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
|
|||
|
||||
bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
|
||||
{
|
||||
if (m_c->m_clipping_plane_distance == 0.f)
|
||||
if (m_c->object_clipper()->get_position() == 0.)
|
||||
return false;
|
||||
|
||||
Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point;
|
||||
transformed_point(2) += m_z_shift;
|
||||
return m_c->m_clipping_plane->is_point_clipped(transformed_point);
|
||||
auto sel_info = m_c->selection_info();
|
||||
int active_inst = m_c->selection_info()->get_active_instance();
|
||||
const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
|
||||
const Transform3d& trafo = mi->get_transformation().get_matrix();
|
||||
|
||||
Vec3d transformed_point = trafo * point;
|
||||
transformed_point(2) += sel_info->get_sla_shift();
|
||||
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -308,27 +217,34 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
|
|||
// Return false if no intersection was found, true otherwise.
|
||||
bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
|
||||
{
|
||||
if (! m_c->m_mesh_raycaster)
|
||||
if (! m_c->raycaster()->raycaster())
|
||||
return false;
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
#else
|
||||
const Camera& camera = m_parent.get_camera();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Geometry::Transformation trafo = volume->get_instance_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
|
||||
|
||||
double clp_dist = m_c->object_clipper()->get_position();
|
||||
const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
|
||||
|
||||
// The raycaster query
|
||||
Vec3f hit;
|
||||
Vec3f normal;
|
||||
if (m_c->m_mesh_raycaster->unproject_on_mesh(
|
||||
if (m_c->raycaster()->raycaster()->unproject_on_mesh(
|
||||
mouse_pos,
|
||||
trafo.get_matrix(),
|
||||
camera,
|
||||
hit,
|
||||
normal,
|
||||
m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr))
|
||||
clp_dist != 0. ? clp : nullptr))
|
||||
{
|
||||
if (m_c->has_drilled_mesh()) {
|
||||
if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) {
|
||||
// in this case the raycaster sees the hollowed and drilled mesh.
|
||||
// if the point lies on the surface created by the hole, we want
|
||||
// to ignore it.
|
||||
|
|
@ -355,6 +271,10 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
|
|||
// concludes that the event was not intended for it, it should return false.
|
||||
bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
|
||||
{
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
int active_inst = m_c->selection_info()->get_active_instance();
|
||||
|
||||
|
||||
// left down with shift - show the selection rectangle:
|
||||
if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
|
||||
if (m_hover_id == -1) {
|
||||
|
|
@ -386,15 +306,15 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
|
|||
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole")));
|
||||
|
||||
Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor();
|
||||
Vec3d scaling = mo->instances[active_inst]->get_scaling_factor();
|
||||
Vec3f normal_transformed(pos_and_normal.second(0)/scaling(0),
|
||||
pos_and_normal.second(1)/scaling(1),
|
||||
pos_and_normal.second(2)/scaling(2));
|
||||
|
||||
m_c->m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + m_c->HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/,
|
||||
mo->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/,
|
||||
-pos_and_normal.second, m_new_hole_radius, m_new_hole_height);
|
||||
m_selected.push_back(false);
|
||||
assert(m_selected.size() == m_c->m_model_object->sla_drain_holes.size());
|
||||
assert(m_selected.size() == mo->sla_drain_holes.size());
|
||||
m_parent.set_as_dirty();
|
||||
m_wait_for_up_event = true;
|
||||
}
|
||||
|
|
@ -413,11 +333,11 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
|
|||
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
|
||||
|
||||
// First collect positions of all the points in world coordinates.
|
||||
Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
Geometry::Transformation trafo = mo->instances[active_inst]->get_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
|
||||
std::vector<Vec3d> points;
|
||||
for (unsigned int i=0; i<m_c->m_model_object->sla_drain_holes.size(); ++i)
|
||||
points.push_back(trafo.get_matrix() * m_c->m_model_object->sla_drain_holes[i].pos.cast<double>());
|
||||
for (unsigned int i=0; i<mo->sla_drain_holes.size(); ++i)
|
||||
points.push_back(trafo.get_matrix() * mo->sla_drain_holes[i].pos.cast<double>());
|
||||
|
||||
// Now ask the rectangle which of the points are inside.
|
||||
std::vector<Vec3f> points_inside;
|
||||
|
|
@ -426,7 +346,9 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
|
|||
points_inside.push_back(points[idx].cast<float>());
|
||||
|
||||
// Only select/deselect points that are actually visible
|
||||
for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get()))
|
||||
for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs(
|
||||
trafo, wxGetApp().plater()->get_camera(), points_inside,
|
||||
m_c->object_clipper()->get_clipping_plane()))
|
||||
{
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
|
|
@ -480,20 +402,21 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
|
|||
}
|
||||
|
||||
if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
|
||||
m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f);
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
m_c->m_clipping_plane_was_moved = true;
|
||||
double pos = m_c->object_clipper()->get_position();
|
||||
pos = std::min(1., pos + 0.01);
|
||||
m_c->object_clipper()->set_position(pos, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
|
||||
m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f);
|
||||
update_clipping_plane(true);
|
||||
double pos = m_c->object_clipper()->get_position();
|
||||
pos = std::max(0., pos - 0.01);
|
||||
m_c->object_clipper()->set_position(pos, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::ResetClippingPlane) {
|
||||
update_clipping_plane();
|
||||
m_c->object_clipper()->set_position(-1., false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -503,11 +426,12 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
|
|||
void GLGizmoHollow::delete_selected_points()
|
||||
{
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole")));
|
||||
sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
|
||||
|
||||
for (unsigned int idx=0; idx<m_c->m_model_object->sla_drain_holes.size(); ++idx) {
|
||||
for (unsigned int idx=0; idx<drain_holes.size(); ++idx) {
|
||||
if (m_selected[idx]) {
|
||||
m_selected.erase(m_selected.begin()+idx);
|
||||
m_c->m_model_object->sla_drain_holes.erase(m_c->m_model_object->sla_drain_holes.begin() + (idx--));
|
||||
drain_holes.erase(drain_holes.begin() + (idx--));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,12 +440,14 @@ void GLGizmoHollow::delete_selected_points()
|
|||
|
||||
void GLGizmoHollow::on_update(const UpdateData& data)
|
||||
{
|
||||
sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
|
||||
|
||||
if (m_hover_id != -1) {
|
||||
std::pair<Vec3f, Vec3f> pos_and_normal;
|
||||
if (! unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal))
|
||||
return;
|
||||
m_c->m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + m_c->HoleStickOutLength * pos_and_normal.second;
|
||||
m_c->m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second;
|
||||
drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second;
|
||||
drain_holes[m_hover_id].normal = -pos_and_normal.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -529,19 +455,22 @@ void GLGizmoHollow::on_update(const UpdateData& data)
|
|||
void GLGizmoHollow::hollow_mesh(bool postpone_error_messages)
|
||||
{
|
||||
wxGetApp().CallAfter([this, postpone_error_messages]() {
|
||||
wxGetApp().plater()->reslice_SLA_hollowing(*m_c->m_model_object, postpone_error_messages);
|
||||
wxGetApp().plater()->reslice_SLA_hollowing(
|
||||
*m_c->selection_info()->model_object(), postpone_error_messages);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> GLGizmoHollow::get_config_options(const std::vector<std::string>& keys) const
|
||||
std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>>
|
||||
GLGizmoHollow::get_config_options(const std::vector<std::string>& keys) const
|
||||
{
|
||||
std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> out;
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
if (!m_c->m_model_object)
|
||||
if (! mo)
|
||||
return out;
|
||||
|
||||
const DynamicPrintConfig& object_cfg = m_c->m_model_object->config;
|
||||
const DynamicPrintConfig& object_cfg = mo->config;
|
||||
const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
|
||||
|
||||
|
|
@ -562,18 +491,10 @@ std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> GLGizmoHollo
|
|||
}
|
||||
|
||||
|
||||
ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const
|
||||
{
|
||||
if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
else
|
||||
return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]);
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
if (! m_c->m_model_object)
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
if (! mo)
|
||||
return;
|
||||
|
||||
bool first_run = true; // This is a hack to redraw the button when all points are removed,
|
||||
|
|
@ -639,7 +560,7 @@ RENDER_AGAIN:
|
|||
auto opts = get_config_options({"hollowing_enable"});
|
||||
m_enable_hollowing = static_cast<const ConfigOptionBool*>(opts[0].first)->value;
|
||||
if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) {
|
||||
m_c->m_model_object->config.opt<ConfigOptionBool>("hollowing_enable", true)->value = m_enable_hollowing;
|
||||
mo->config.opt<ConfigOptionBool>("hollowing_enable", true)->value = m_enable_hollowing;
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
config_changed = true;
|
||||
}
|
||||
|
|
@ -701,14 +622,14 @@ RENDER_AGAIN:
|
|||
}
|
||||
if (slider_edited || slider_released) {
|
||||
if (slider_released) {
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = m_offset_stash;
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = m_quality_stash;
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = m_closing_d_stash;
|
||||
mo->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = m_offset_stash;
|
||||
mo->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = m_quality_stash;
|
||||
mo->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = m_closing_d_stash;
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change")));
|
||||
}
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = offset;
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = quality;
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = closing_d;
|
||||
mo->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = offset;
|
||||
mo->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = quality;
|
||||
mo->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = closing_d;
|
||||
if (slider_released) {
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
config_changed = true;
|
||||
|
|
@ -739,9 +660,9 @@ RENDER_AGAIN:
|
|||
|
||||
m_imgui->text(m_desc["hole_depth"]);
|
||||
ImGui::SameLine(diameter_slider_left);
|
||||
m_new_hole_height -= m_c->HoleStickOutLength;
|
||||
m_new_hole_height -= HoleStickOutLength;
|
||||
ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm");
|
||||
m_new_hole_height += m_c->HoleStickOutLength;
|
||||
m_new_hole_height += HoleStickOutLength;
|
||||
|
||||
clicked |= ImGui::IsItemClicked();
|
||||
edited |= ImGui::IsItemEdited();
|
||||
|
|
@ -753,19 +674,19 @@ RENDER_AGAIN:
|
|||
// - take correct undo/redo snapshot after the user is done with moving the slider
|
||||
if (! m_selection_empty) {
|
||||
if (clicked) {
|
||||
m_holes_stash = m_c->m_model_object->sla_drain_holes;
|
||||
m_holes_stash = mo->sla_drain_holes;
|
||||
}
|
||||
if (edited) {
|
||||
for (size_t idx=0; idx<m_selected.size(); ++idx)
|
||||
if (m_selected[idx]) {
|
||||
m_c->m_model_object->sla_drain_holes[idx].radius = m_new_hole_radius;
|
||||
m_c->m_model_object->sla_drain_holes[idx].height = m_new_hole_height;
|
||||
mo->sla_drain_holes[idx].radius = m_new_hole_radius;
|
||||
mo->sla_drain_holes[idx].height = m_new_hole_height;
|
||||
}
|
||||
}
|
||||
if (deactivated) {
|
||||
// momentarily restore the old value to take snapshot
|
||||
sla::DrainHoles new_holes = m_c->m_model_object->sla_drain_holes;
|
||||
m_c->m_model_object->sla_drain_holes = m_holes_stash;
|
||||
sla::DrainHoles new_holes = mo->sla_drain_holes;
|
||||
mo->sla_drain_holes = m_holes_stash;
|
||||
float backup_rad = m_new_hole_radius;
|
||||
float backup_hei = m_new_hole_height;
|
||||
for (size_t i=0; i<m_holes_stash.size(); ++i) {
|
||||
|
|
@ -778,7 +699,7 @@ RENDER_AGAIN:
|
|||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter")));
|
||||
m_new_hole_radius = backup_rad;
|
||||
m_new_hole_height = backup_hei;
|
||||
m_c->m_model_object->sla_drain_holes = new_holes;
|
||||
mo->sla_drain_holes = new_holes;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -786,33 +707,33 @@ RENDER_AGAIN:
|
|||
remove_selected = m_imgui->button(m_desc.at("remove_selected"));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->disabled_begin(m_c->m_model_object->sla_drain_holes.empty());
|
||||
m_imgui->disabled_begin(mo->sla_drain_holes.empty());
|
||||
remove_all = m_imgui->button(m_desc.at("remove_all"));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
// Following is rendered in both editing and non-editing mode:
|
||||
// m_imgui->text("");
|
||||
ImGui::Separator();
|
||||
if (m_c->m_clipping_plane_distance == 0.f)
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
update_clipping_plane();
|
||||
m_c->object_clipper()->set_position(-1., false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) {
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
m_c->m_clipping_plane_was_moved = true;
|
||||
}
|
||||
float clp_dist = m_c->object_clipper()->get_position();
|
||||
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
// make sure supports are shown/hidden as appropriate
|
||||
if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) {
|
||||
m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance);
|
||||
bool show_sups = m_c->instances_hider()->are_supports_shown();
|
||||
if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) {
|
||||
m_c->instances_hider()->show_supports(show_sups);
|
||||
force_refresh = true;
|
||||
}
|
||||
|
||||
|
|
@ -871,54 +792,30 @@ std::string GLGizmoHollow::on_get_name() const
|
|||
}
|
||||
|
||||
|
||||
CommonGizmosDataID GLGizmoHollow::on_get_requirements() const
|
||||
{
|
||||
return CommonGizmosDataID(
|
||||
int(CommonGizmosDataID::SelectionInfo)
|
||||
| int(CommonGizmosDataID::InstancesHider)
|
||||
| int(CommonGizmosDataID::Raycaster)
|
||||
| int(CommonGizmosDataID::HollowedMesh)
|
||||
| int(CommonGizmosDataID::ObjectClipper)
|
||||
| int(CommonGizmosDataID::SupportsClipper));
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoHollow::on_set_state()
|
||||
{
|
||||
// m_c->m_model_object pointer can be invalid (for instance because of undo/redo action),
|
||||
// we should recover it from the object id
|
||||
m_c->m_model_object = nullptr;
|
||||
for (const auto mo : wxGetApp().model().objects) {
|
||||
if (mo->id() == m_c->m_model_object_id) {
|
||||
m_c->m_model_object = mo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state == m_old_state)
|
||||
return;
|
||||
|
||||
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
|
||||
//Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on")));
|
||||
//m_c->update_from_backend(m_parent, m_c->m_model_object);
|
||||
m_c->unstash_clipping_plane();
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
|
||||
m_c->build_AABB_if_needed();
|
||||
|
||||
// we'll now reload support points:
|
||||
if (m_c->m_model_object)
|
||||
if (m_c->selection_info()->model_object())
|
||||
reload_cache();
|
||||
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
if (m_c->m_model_object) {
|
||||
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
|
||||
m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance);
|
||||
}
|
||||
}
|
||||
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
|
||||
//Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
|
||||
if (m_state == Off && m_old_state != Off) // the gizmo was just turned Off
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
m_c->stash_clipping_plane();
|
||||
m_c->m_clipping_plane_distance = 0.f;
|
||||
update_clipping_plane(true);
|
||||
// Release clippers and the AABB raycaster.
|
||||
m_c->m_object_clipper.reset();
|
||||
m_c->m_supports_clipper.reset();
|
||||
//m_c->m_mesh_raycaster.reset();
|
||||
//m_c->m_cavity_mesh.reset();
|
||||
//m_c->m_volume_with_cavity.reset();
|
||||
}
|
||||
m_old_state = m_state;
|
||||
}
|
||||
|
||||
|
|
@ -929,7 +826,7 @@ void GLGizmoHollow::on_start_dragging()
|
|||
if (m_hover_id != -1) {
|
||||
select_point(NoPoints);
|
||||
select_point(m_hover_id);
|
||||
m_hole_before_drag = m_c->m_model_object->sla_drain_holes[m_hover_id].pos;
|
||||
m_hole_before_drag = m_c->selection_info()->model_object()->sla_drain_holes[m_hover_id].pos;
|
||||
}
|
||||
else
|
||||
m_hole_before_drag = Vec3f::Zero();
|
||||
|
|
@ -938,15 +835,16 @@ void GLGizmoHollow::on_start_dragging()
|
|||
|
||||
void GLGizmoHollow::on_stop_dragging()
|
||||
{
|
||||
sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
|
||||
if (m_hover_id != -1) {
|
||||
Vec3f backup = m_c->m_model_object->sla_drain_holes[m_hover_id].pos;
|
||||
Vec3f backup = drain_holes[m_hover_id].pos;
|
||||
|
||||
if (m_hole_before_drag != Vec3f::Zero() // some point was touched
|
||||
&& backup != m_hole_before_drag) // and it was moved, not just selected
|
||||
{
|
||||
m_c->m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag;
|
||||
drain_holes[m_hover_id].pos = m_hole_before_drag;
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole")));
|
||||
m_c->m_model_object->sla_drain_holes[m_hover_id].pos = backup;
|
||||
drain_holes[m_hover_id].pos = backup;
|
||||
}
|
||||
}
|
||||
m_hole_before_drag = Vec3f::Zero();
|
||||
|
|
@ -956,10 +854,7 @@ void GLGizmoHollow::on_stop_dragging()
|
|||
|
||||
void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar)
|
||||
{
|
||||
ar(m_c->m_clipping_plane_distance,
|
||||
*m_c->m_clipping_plane,
|
||||
m_c->m_model_object_id,
|
||||
m_new_hole_radius,
|
||||
ar(m_new_hole_radius,
|
||||
m_new_hole_height,
|
||||
m_selected,
|
||||
m_selection_empty
|
||||
|
|
@ -970,10 +865,7 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar)
|
|||
|
||||
void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const
|
||||
{
|
||||
ar(m_c->m_clipping_plane_distance,
|
||||
*m_c->m_clipping_plane,
|
||||
m_c->m_model_object_id,
|
||||
m_new_hole_radius,
|
||||
ar(m_new_hole_radius,
|
||||
m_new_hole_height,
|
||||
m_selected,
|
||||
m_selection_empty
|
||||
|
|
@ -984,13 +876,15 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const
|
|||
|
||||
void GLGizmoHollow::select_point(int i)
|
||||
{
|
||||
const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
|
||||
|
||||
if (i == AllPoints || i == NoPoints) {
|
||||
m_selected.assign(m_selected.size(), i == AllPoints);
|
||||
m_selection_empty = (i == NoPoints);
|
||||
|
||||
if (i == AllPoints) {
|
||||
m_new_hole_radius = m_c->m_model_object->sla_drain_holes[0].radius;
|
||||
m_new_hole_height = m_c->m_model_object->sla_drain_holes[0].height;
|
||||
m_new_hole_radius = drain_holes[0].radius;
|
||||
m_new_hole_height = drain_holes[0].height;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -998,8 +892,8 @@ void GLGizmoHollow::select_point(int i)
|
|||
m_selected.push_back(false);
|
||||
m_selected[i] = true;
|
||||
m_selection_empty = false;
|
||||
m_new_hole_radius = m_c->m_model_object->sla_drain_holes[i].radius;
|
||||
m_new_hole_height = m_c->m_model_object->sla_drain_holes[i].height;
|
||||
m_new_hole_radius = drain_holes[i].radius;
|
||||
m_new_hole_height = drain_holes[i].height;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1019,26 +913,13 @@ void GLGizmoHollow::unselect_point(int i)
|
|||
void GLGizmoHollow::reload_cache()
|
||||
{
|
||||
m_selected.clear();
|
||||
m_selected.assign(m_c->m_model_object->sla_drain_holes.size(), false);
|
||||
}
|
||||
|
||||
void GLGizmoHollow::update_clipping_plane(bool keep_normal) const
|
||||
{
|
||||
if (! m_c->m_model_object)
|
||||
return;
|
||||
Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ?
|
||||
m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward());
|
||||
|
||||
const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift);
|
||||
float dist = normal.dot(center);
|
||||
*m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius));
|
||||
m_parent.set_as_dirty();
|
||||
m_selected.assign(m_c->selection_info()->model_object()->sla_drain_holes.size(), false);
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoHollow::on_set_hover_id()
|
||||
{
|
||||
if (int(m_c->m_model_object->sla_drain_holes.size()) <= m_hover_id)
|
||||
if (int(m_c->selection_info()->model_object()->sla_drain_holes.size()) <= m_hover_id)
|
||||
m_hover_id = -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,16 +13,11 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class ClippingPlane;
|
||||
class MeshClipper;
|
||||
class MeshRaycaster;
|
||||
class CommonGizmosData;
|
||||
enum class SLAGizmoEventType : unsigned char;
|
||||
|
||||
class GLGizmoHollow : public GLGizmoBase
|
||||
{
|
||||
private:
|
||||
mutable double m_z_shift = 0.;
|
||||
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
|
||||
|
||||
GLUquadricObj* m_quadric;
|
||||
|
|
@ -33,12 +28,10 @@ public:
|
|||
~GLGizmoHollow() override;
|
||||
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
void delete_selected_points();
|
||||
ClippingPlane get_sla_clipping_plane() const;
|
||||
|
||||
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); }
|
||||
void update_clipping_plane(bool keep_normal = false) const;
|
||||
void set_common_data_ptr(CommonGizmosData* ptr) { m_c = ptr; }
|
||||
void delete_selected_points();
|
||||
bool is_selection_rectangle_dragging() const {
|
||||
return m_selection_rectangle.is_dragging();
|
||||
}
|
||||
|
||||
private:
|
||||
bool on_init() override;
|
||||
|
|
@ -47,11 +40,10 @@ private:
|
|||
void on_render_for_picking() const override;
|
||||
|
||||
void render_points(const Selection& selection, bool picking = false) const;
|
||||
void render_clipping_plane(const Selection& selection) const;
|
||||
void hollow_mesh(bool postpone_error_messages = false);
|
||||
bool unsaved_changes() const;
|
||||
|
||||
bool m_show_supports = true;
|
||||
// bool m_show_supports = true;
|
||||
float m_new_hole_radius = 2.f; // Size of a new hole.
|
||||
float m_new_hole_height = 6.f;
|
||||
mutable std::vector<bool> m_selected; // which holes are currently selected
|
||||
|
|
@ -67,10 +59,6 @@ private:
|
|||
sla::DrainHoles m_holes_in_drilled_mesh;
|
||||
|
||||
sla::DrainHoles m_holes_stash;
|
||||
|
||||
CommonGizmosData* m_c = nullptr;
|
||||
|
||||
//std::unique_ptr<ClippingPlane> m_clipping_plane;
|
||||
|
||||
// This map holds all translated description texts, so they can be easily referenced during layout calculations
|
||||
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
|
||||
|
|
@ -101,6 +89,7 @@ protected:
|
|||
void on_start_dragging() override;
|
||||
void on_stop_dragging() override;
|
||||
void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
virtual CommonGizmosDataID on_get_requirements() const override;
|
||||
|
||||
std::string on_get_name() const override;
|
||||
bool on_is_activable() const override;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,22 @@ GLGizmoMove3D::~GLGizmoMove3D()
|
|||
::gluDeleteQuadric(m_quadric);
|
||||
}
|
||||
|
||||
std::string GLGizmoMove3D::get_tooltip() const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
bool show_position = selection.is_single_full_instance();
|
||||
const Vec3d& position = selection.get_bounding_box().center();
|
||||
|
||||
if (m_hover_id == 0 || m_grabbers[0].dragging)
|
||||
return "X: " + format(show_position ? position(0) : m_displacement(0), 2);
|
||||
else if (m_hover_id == 1 || m_grabbers[1].dragging)
|
||||
return "Y: " + format(show_position ? position(1) : m_displacement(1), 2);
|
||||
else if (m_hover_id == 2 || m_grabbers[2].dragging)
|
||||
return "Z: " + format(show_position ? position(2) : m_displacement(2), 2);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
bool GLGizmoMove3D::on_init()
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
|
|
@ -85,22 +101,6 @@ void GLGizmoMove3D::on_render() const
|
|||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
|
||||
bool show_position = selection.is_single_full_instance();
|
||||
const Vec3d& position = selection.get_bounding_box().center();
|
||||
|
||||
if ((show_position && (m_hover_id == 0)) || m_grabbers[0].dragging)
|
||||
set_tooltip("X: " + format(show_position ? position(0) : m_displacement(0), 2));
|
||||
else if (!m_grabbers[0].dragging && (m_hover_id == 0))
|
||||
set_tooltip("X");
|
||||
else if ((show_position && (m_hover_id == 1)) || m_grabbers[1].dragging)
|
||||
set_tooltip("Y: " + format(show_position ? position(1) : m_displacement(1), 2));
|
||||
else if (!m_grabbers[1].dragging && (m_hover_id == 1))
|
||||
set_tooltip("Y");
|
||||
else if ((show_position && (m_hover_id == 2)) || m_grabbers[2].dragging)
|
||||
set_tooltip("Z: " + format(show_position ? position(2) : m_displacement(2), 2));
|
||||
else if (!m_grabbers[2].dragging && (m_hover_id == 2))
|
||||
set_tooltip("Z");
|
||||
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ public:
|
|||
|
||||
const Vec3d& get_displacement() const { return m_displacement; }
|
||||
|
||||
std::string get_tooltip() const override;
|
||||
|
||||
protected:
|
||||
virtual bool on_init();
|
||||
virtual std::string on_get_name() const;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,18 @@ void GLGizmoRotate::set_angle(double angle)
|
|||
m_angle = angle;
|
||||
}
|
||||
|
||||
std::string GLGizmoRotate::get_tooltip() const
|
||||
{
|
||||
std::string axis;
|
||||
switch (m_axis)
|
||||
{
|
||||
case X: { axis = "X"; break; }
|
||||
case Y: { axis = "Y"; break; }
|
||||
case Z: { axis = "Z"; break; }
|
||||
}
|
||||
return (m_hover_id == 0 || m_grabbers[0].dragging) ? axis + ": " + format((float)Geometry::rad2deg(m_angle), 4) : "";
|
||||
}
|
||||
|
||||
bool GLGizmoRotate::on_init()
|
||||
{
|
||||
m_grabbers.push_back(Grabber());
|
||||
|
|
@ -127,19 +139,7 @@ void GLGizmoRotate::on_render() const
|
|||
const Selection& selection = m_parent.get_selection();
|
||||
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||
|
||||
std::string axis;
|
||||
switch (m_axis)
|
||||
{
|
||||
case X: { axis = "X"; break; }
|
||||
case Y: { axis = "Y"; break; }
|
||||
case Z: { axis = "Z"; break; }
|
||||
}
|
||||
|
||||
if (!m_dragging && (m_hover_id == 0))
|
||||
set_tooltip(axis);
|
||||
else if (m_dragging)
|
||||
set_tooltip(axis + ": " + format((float)Geometry::rad2deg(m_angle), 4) + "\u00B0");
|
||||
else
|
||||
if (m_hover_id != 0 && !m_grabbers[0].dragging)
|
||||
{
|
||||
m_center = box.center();
|
||||
m_radius = Offset + box.radius();
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ public:
|
|||
double get_angle() const { return m_angle; }
|
||||
void set_angle(double angle);
|
||||
|
||||
std::string get_tooltip() const override;
|
||||
|
||||
protected:
|
||||
virtual bool on_init();
|
||||
virtual std::string on_get_name() const { return ""; }
|
||||
|
|
@ -81,6 +83,16 @@ public:
|
|||
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
|
||||
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
|
||||
|
||||
std::string get_tooltip() const override
|
||||
{
|
||||
std::string tooltip = m_gizmos[X].get_tooltip();
|
||||
if (tooltip.empty())
|
||||
tooltip = m_gizmos[Y].get_tooltip();
|
||||
if (tooltip.empty())
|
||||
tooltip = m_gizmos[Z].get_tooltip();
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool on_init();
|
||||
virtual std::string on_get_name() const;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,38 @@ GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filen
|
|||
{
|
||||
}
|
||||
|
||||
std::string GLGizmoScale3D::get_tooltip() const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
|
||||
bool single_instance = selection.is_single_full_instance();
|
||||
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
|
||||
bool single_selection = single_instance || single_volume;
|
||||
|
||||
Vec3f scale = 100.0f * Vec3f::Ones();
|
||||
if (single_instance)
|
||||
scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast<float>();
|
||||
else if (single_volume)
|
||||
scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast<float>();
|
||||
|
||||
if (m_hover_id == 0 || m_hover_id == 1 || m_grabbers[0].dragging || m_grabbers[1].dragging)
|
||||
return "X: " + format(scale(0), 4) + "%";
|
||||
else if (m_hover_id == 2 || m_hover_id == 3 || m_grabbers[2].dragging || m_grabbers[3].dragging)
|
||||
return "Y: " + format(scale(1), 4) + "%";
|
||||
else if (m_hover_id == 4 || m_hover_id == 5 || m_grabbers[4].dragging || m_grabbers[5].dragging)
|
||||
return "Z: " + format(scale(2), 4) + "%";
|
||||
else if (m_hover_id == 6 || m_hover_id == 7 || m_hover_id == 8 || m_hover_id == 9 ||
|
||||
m_grabbers[6].dragging || m_grabbers[7].dragging || m_grabbers[8].dragging || m_grabbers[9].dragging)
|
||||
{
|
||||
std::string tooltip = "X: " + format(scale(0), 4) + "%\n";
|
||||
tooltip += "Y: " + format(scale(1), 4) + "%\n";
|
||||
tooltip += "Z: " + format(scale(2), 4) + "%";
|
||||
return tooltip;
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
bool GLGizmoScale3D::on_init()
|
||||
{
|
||||
for (int i = 0; i < 10; ++i)
|
||||
|
|
@ -89,37 +121,6 @@ void GLGizmoScale3D::on_render() const
|
|||
|
||||
bool single_instance = selection.is_single_full_instance();
|
||||
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
|
||||
bool single_selection = single_instance || single_volume;
|
||||
|
||||
Vec3f scale = 100.0f * Vec3f::Ones();
|
||||
if (single_instance)
|
||||
scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast<float>();
|
||||
else if (single_volume)
|
||||
scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast<float>();
|
||||
|
||||
if ((single_selection && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging)
|
||||
set_tooltip("X: " + format(scale(0), 4) + "%");
|
||||
else if (!m_grabbers[0].dragging && !m_grabbers[1].dragging && ((m_hover_id == 0) || (m_hover_id == 1)))
|
||||
set_tooltip("X");
|
||||
else if ((single_selection && ((m_hover_id == 2) || (m_hover_id == 3))) || m_grabbers[2].dragging || m_grabbers[3].dragging)
|
||||
set_tooltip("Y: " + format(scale(1), 4) + "%");
|
||||
else if (!m_grabbers[2].dragging && !m_grabbers[3].dragging && ((m_hover_id == 2) || (m_hover_id == 3)))
|
||||
set_tooltip("Y");
|
||||
else if ((single_selection && ((m_hover_id == 4) || (m_hover_id == 5))) || m_grabbers[4].dragging || m_grabbers[5].dragging)
|
||||
set_tooltip("Z: " + format(scale(2), 4) + "%");
|
||||
else if (!m_grabbers[4].dragging && !m_grabbers[5].dragging && ((m_hover_id == 4) || (m_hover_id == 5)))
|
||||
set_tooltip("Z");
|
||||
else if ((single_selection && ((m_hover_id == 6) || (m_hover_id == 7) || (m_hover_id == 8) || (m_hover_id == 9)))
|
||||
|| m_grabbers[6].dragging || m_grabbers[7].dragging || m_grabbers[8].dragging || m_grabbers[9].dragging)
|
||||
{
|
||||
std::string tooltip = "X: " + format(scale(0), 4) + "%\n";
|
||||
tooltip += "Y: " + format(scale(1), 4) + "%\n";
|
||||
tooltip += "Z: " + format(scale(2), 4) + "%";
|
||||
set_tooltip(tooltip);
|
||||
}
|
||||
else if (!m_grabbers[6].dragging && !m_grabbers[7].dragging && !m_grabbers[8].dragging && !m_grabbers[9].dragging &&
|
||||
((m_hover_id == 6) || (m_hover_id == 7) || (m_hover_id == 8) || (m_hover_id == 9)))
|
||||
set_tooltip("X/Y/Z");
|
||||
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ public:
|
|||
|
||||
const Vec3d& get_offset() const { return m_offset; }
|
||||
|
||||
std::string get_tooltip() const override;
|
||||
|
||||
protected:
|
||||
virtual bool on_init();
|
||||
virtual std::string on_get_name() const;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#include "GLGizmoSlaSupports.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectSettings.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/MeshUtils.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/PresetBundle.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
|
|
@ -26,7 +25,6 @@ namespace GUI {
|
|||
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
|
||||
: GLGizmoBase(parent, icon_filename, sprite_id)
|
||||
, m_quadric(nullptr)
|
||||
, m_its(nullptr)
|
||||
{
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
|
|
@ -63,26 +61,21 @@ bool GLGizmoSlaSupports::on_init()
|
|||
|
||||
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection)
|
||||
{
|
||||
if (m_c->recent_update) {
|
||||
if (m_state == On)
|
||||
m_c->build_AABB_if_needed();
|
||||
if (! m_c->selection_info())
|
||||
return;
|
||||
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
if (mo != m_old_mo) {
|
||||
disable_editing_mode();
|
||||
if (m_c->m_model_object)
|
||||
if (mo)
|
||||
reload_cache();
|
||||
}
|
||||
|
||||
if (m_state == On) {
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
|
||||
m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance);
|
||||
m_old_mo = mo;
|
||||
}
|
||||
|
||||
// If we triggered autogeneration before, check backend and fetch results if they are there
|
||||
if (m_c->m_model_object) {
|
||||
if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating)
|
||||
if (mo) {
|
||||
if (mo->sla_points_status == sla::PointsStatus::Generating)
|
||||
get_data_from_backend();
|
||||
}
|
||||
}
|
||||
|
|
@ -91,13 +84,13 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
|
|||
|
||||
void GLGizmoSlaSupports::on_render() const
|
||||
{
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
|
||||
// If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off
|
||||
if (m_state == On
|
||||
&& (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()]
|
||||
|| m_c->m_active_instance != selection.get_instance_idx()
|
||||
|| m_c->m_model_object_id != m_c->m_model_object->id())) {
|
||||
&& (mo != selection.get_model()->objects[selection.get_object_idx()]
|
||||
|| m_c->selection_info()->get_active_instance() != selection.get_instance_idx())) {
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS));
|
||||
return;
|
||||
}
|
||||
|
|
@ -105,113 +98,20 @@ void GLGizmoSlaSupports::on_render() const
|
|||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
|
||||
|
||||
if (m_quadric != nullptr && selection.is_from_single_instance())
|
||||
render_points(selection, false);
|
||||
|
||||
m_selection_rectangle.render(m_parent);
|
||||
render_clipping_plane(selection);
|
||||
m_c->object_clipper()->render_cut();
|
||||
m_c->supports_clipper()->render_cut();
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
|
||||
{
|
||||
if (m_c->m_clipping_plane_distance == 0.f || m_c->m_mesh->empty())
|
||||
return;
|
||||
|
||||
// Get transformation of the instance
|
||||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Geometry::Transformation trafo = vol->get_instance_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
|
||||
// Get transformation of supports
|
||||
Geometry::Transformation supports_trafo;
|
||||
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z()));
|
||||
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
|
||||
// I don't know why, but following seems to be correct.
|
||||
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
|
||||
1,
|
||||
1.));
|
||||
|
||||
// Now initialize the TMS for the object, perform the cut and save the result.
|
||||
if (! m_c->m_object_clipper) {
|
||||
m_c->m_object_clipper.reset(new MeshClipper);
|
||||
m_c->m_object_clipper->set_mesh(*m_c->mesh());
|
||||
}
|
||||
m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane);
|
||||
m_c->m_object_clipper->set_transformation(trafo);
|
||||
|
||||
|
||||
// Next, ask the backend if supports are already calculated. If so, we are gonna cut them too.
|
||||
// First we need a pointer to the respective SLAPrintObject. The index into objects vector is
|
||||
// cached so we don't have todo it on each render. We only search for the po if needed:
|
||||
if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) {
|
||||
m_c->m_print_objects_count = m_parent.sla_print()->objects().size();
|
||||
m_c->m_print_object_idx = -1;
|
||||
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
|
||||
++m_c->m_print_object_idx;
|
||||
if (po->model_object()->id() == m_c->m_model_object->id())
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_c->m_print_object_idx >= 0) {
|
||||
const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx];
|
||||
|
||||
if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) {
|
||||
// If the supports are already calculated, save the timestamp of the respective step
|
||||
// so we can later tell they were recalculated.
|
||||
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
|
||||
|
||||
if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) {
|
||||
// The timestamp has changed.
|
||||
m_c->m_supports_clipper.reset(new MeshClipper);
|
||||
// The mesh should already have the shared vertices calculated.
|
||||
m_c->m_supports_clipper->set_mesh(print_object->support_mesh());
|
||||
m_c->m_old_timestamp = timestamp;
|
||||
}
|
||||
m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane);
|
||||
m_c->m_supports_clipper->set_transformation(supports_trafo);
|
||||
}
|
||||
else
|
||||
// The supports are not valid. We better dump the cached data.
|
||||
m_c->m_supports_clipper.reset();
|
||||
}
|
||||
|
||||
// At this point we have the triangulated cuts for both the object and supports - let's render.
|
||||
if (! m_c->m_object_clipper->get_triangles().empty()) {
|
||||
::glPushMatrix();
|
||||
::glColor3f(1.0f, 0.37f, 0.0f);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
for (const Vec3f& point : m_c->m_object_clipper->get_triangles())
|
||||
::glVertex3f(point(0), point(1), point(2));
|
||||
::glEnd();
|
||||
::glPopMatrix();
|
||||
}
|
||||
|
||||
if (m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty() && !m_editing_mode) {
|
||||
// The supports are hidden in the editing mode, so it makes no sense to render the cuts.
|
||||
::glPushMatrix();
|
||||
::glColor3f(1.0f, 0.f, 0.37f);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
for (const Vec3f& point : m_c->m_supports_clipper->get_triangles())
|
||||
::glVertex3f(point(0), point(1), point(2));
|
||||
::glEnd();
|
||||
::glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::on_render_for_picking() const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
#if ENABLE_RENDER_PICKING_PASS
|
||||
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
|
||||
#endif
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
render_points(selection, true);
|
||||
}
|
||||
|
|
@ -224,9 +124,10 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
|
||||
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
|
||||
float z_shift = m_c->selection_info()->get_sla_shift();
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(0.0, 0.0, m_z_shift));
|
||||
glsafe(::glTranslated(0.0, 0.0, z_shift));
|
||||
glsafe(::glMultMatrixd(instance_matrix.data()));
|
||||
|
||||
float render_color[4];
|
||||
|
|
@ -282,7 +183,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
if (m_editing_mode) {
|
||||
// in case the normal is not yet cached, find and cache it
|
||||
if (m_editing_cache[i].normal == Vec3f::Zero())
|
||||
m_c->m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
|
||||
m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
|
||||
|
||||
Eigen::Quaterniond q;
|
||||
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>());
|
||||
|
|
@ -312,14 +213,15 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
}
|
||||
|
||||
// Now render the drain holes:
|
||||
if (! m_c->has_drilled_mesh()) {
|
||||
//if (! m_c->has_drilled_mesh()) {
|
||||
if (! m_c->hollowed_mesh()->get_hollowed_mesh()) {
|
||||
render_color[0] = 0.7f;
|
||||
render_color[1] = 0.7f;
|
||||
render_color[2] = 0.7f;
|
||||
render_color[3] = 0.7f;
|
||||
glsafe(::glColor4fv(render_color));
|
||||
for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) {
|
||||
if (is_mesh_point_clipped((drain_hole.pos+m_c->HoleStickOutLength*drain_hole.normal).cast<double>()))
|
||||
for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
|
||||
if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast<double>()))
|
||||
continue;
|
||||
|
||||
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
|
||||
|
|
@ -362,12 +264,17 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
|
||||
bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
|
||||
{
|
||||
if (m_c->m_clipping_plane_distance == 0.f)
|
||||
if (m_c->object_clipper()->get_position() == 0.)
|
||||
return false;
|
||||
|
||||
Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point;
|
||||
transformed_point(2) += m_z_shift;
|
||||
return m_c->m_clipping_plane->is_point_clipped(transformed_point);
|
||||
auto sel_info = m_c->selection_info();
|
||||
int active_inst = m_c->selection_info()->get_active_instance();
|
||||
const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
|
||||
const Transform3d& trafo = mi->get_transformation().get_matrix();
|
||||
|
||||
Vec3d transformed_point = trafo * point;
|
||||
transformed_point(2) += sel_info->get_sla_shift();
|
||||
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -376,33 +283,37 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
|
|||
// Return false if no intersection was found, true otherwise.
|
||||
bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
|
||||
{
|
||||
if (! m_c->m_mesh_raycaster)
|
||||
if (! m_c->raycaster()->raycaster())
|
||||
return false;
|
||||
|
||||
const Camera& camera = m_parent.get_camera();
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Geometry::Transformation trafo = volume->get_instance_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
|
||||
|
||||
double clp_dist = m_c->object_clipper()->get_position();
|
||||
const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
|
||||
|
||||
// The raycaster query
|
||||
Vec3f hit;
|
||||
Vec3f normal;
|
||||
if (m_c->m_mesh_raycaster->unproject_on_mesh(
|
||||
if (m_c->raycaster()->raycaster()->unproject_on_mesh(
|
||||
mouse_pos,
|
||||
trafo.get_matrix(),
|
||||
camera,
|
||||
hit,
|
||||
normal,
|
||||
m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr))
|
||||
clp_dist != 0. ? clp : nullptr))
|
||||
{
|
||||
// Check whether the hit is in a hole
|
||||
bool in_hole = false;
|
||||
// In case the hollowed and drilled mesh is available, we can allow
|
||||
// placing points in holes, because they should never end up
|
||||
// on surface that's been drilled away.
|
||||
if (! m_c->has_drilled_mesh()) {
|
||||
for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) {
|
||||
if (! m_c->hollowed_mesh()->get_hollowed_mesh()) {
|
||||
sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
|
||||
for (const sla::DrainHole& hole : drain_holes) {
|
||||
if (hole.is_inside(hit)) {
|
||||
in_hole = true;
|
||||
break;
|
||||
|
|
@ -425,6 +336,9 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
|
|||
// concludes that the event was not intended for it, it should return false.
|
||||
bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
|
||||
{
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
int active_inst = m_c->selection_info()->get_active_instance();
|
||||
|
||||
if (m_editing_mode) {
|
||||
|
||||
// left down with shift - show the selection rectangle:
|
||||
|
|
@ -476,8 +390,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
|
||||
|
||||
// First collect positions of all the points in world coordinates.
|
||||
Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
Geometry::Transformation trafo = mo->instances[active_inst]->get_transformation();
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
|
||||
std::vector<Vec3d> points;
|
||||
for (unsigned int i=0; i<m_editing_cache.size(); ++i)
|
||||
points.push_back(trafo.get_matrix() * m_editing_cache[i].support_point.pos.cast<double>());
|
||||
|
|
@ -489,7 +403,9 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
points_inside.push_back(points[idx].cast<float>());
|
||||
|
||||
// Only select/deselect points that are actually visible
|
||||
for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get()))
|
||||
for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs(
|
||||
trafo, wxGetApp().plater()->get_camera(), points_inside,
|
||||
m_c->object_clipper()->get_clipping_plane()))
|
||||
{
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
|
|
@ -566,20 +482,21 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
}
|
||||
|
||||
if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
|
||||
m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f);
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
m_c->m_clipping_plane_was_moved = true;
|
||||
double pos = m_c->object_clipper()->get_position();
|
||||
pos = std::min(1., pos + 0.01);
|
||||
m_c->object_clipper()->set_position(pos, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
|
||||
m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f);
|
||||
update_clipping_plane(true);
|
||||
double pos = m_c->object_clipper()->get_position();
|
||||
pos = std::max(0., pos - 0.01);
|
||||
m_c->object_clipper()->set_position(pos, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::ResetClippingPlane) {
|
||||
update_clipping_plane();
|
||||
m_c->object_clipper()->set_position(-1., false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -623,11 +540,12 @@ void GLGizmoSlaSupports::on_update(const UpdateData& data)
|
|||
std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
|
||||
{
|
||||
std::vector<const ConfigOption*> out;
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
if (!m_c->m_model_object)
|
||||
if (! mo)
|
||||
return out;
|
||||
|
||||
const DynamicPrintConfig& object_cfg = m_c->m_model_object->config;
|
||||
const DynamicPrintConfig& object_cfg = mo->config;
|
||||
const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
|
||||
|
||||
|
|
@ -648,14 +566,6 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st
|
|||
}
|
||||
|
||||
|
||||
ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const
|
||||
{
|
||||
if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
else
|
||||
return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& idxs) const
|
||||
|
|
@ -703,7 +613,9 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
static float last_y = 0.0f;
|
||||
static float last_h = 0.0f;
|
||||
|
||||
if (! m_c->m_model_object)
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
if (! mo)
|
||||
return;
|
||||
|
||||
bool first_run = true; // This is a hack to redraw the button when all points are removed,
|
||||
|
|
@ -840,15 +752,15 @@ RENDER_AGAIN:
|
|||
m_density_stash = density;
|
||||
}
|
||||
if (slider_edited) {
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
|
||||
m_c->m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
|
||||
mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
|
||||
mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
|
||||
}
|
||||
if (slider_released) {
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash;
|
||||
m_c->m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)m_density_stash;
|
||||
mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash;
|
||||
mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)m_density_stash;
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change")));
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
|
||||
m_c->m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
|
||||
mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
|
||||
mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
}
|
||||
|
||||
|
|
@ -875,7 +787,7 @@ RENDER_AGAIN:
|
|||
|
||||
// Following is rendered in both editing and non-editing mode:
|
||||
ImGui::Separator();
|
||||
if (m_c->m_clipping_plane_distance == 0.f)
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
{
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
|
|
@ -883,17 +795,16 @@ RENDER_AGAIN:
|
|||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
update_clipping_plane();
|
||||
m_c->object_clipper()->set_position(-1., false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) {
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
m_c->m_clipping_plane_was_moved = true;
|
||||
}
|
||||
float clp_dist = m_c->object_clipper()->get_position();
|
||||
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
|
||||
if (m_imgui->button("?")) {
|
||||
|
|
@ -958,18 +869,22 @@ std::string GLGizmoSlaSupports::on_get_name() const
|
|||
}
|
||||
|
||||
|
||||
CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
|
||||
{
|
||||
return CommonGizmosDataID(
|
||||
int(CommonGizmosDataID::SelectionInfo)
|
||||
| int(CommonGizmosDataID::InstancesHider)
|
||||
| int(CommonGizmosDataID::Raycaster)
|
||||
| int(CommonGizmosDataID::HollowedMesh)
|
||||
| int(CommonGizmosDataID::ObjectClipper)
|
||||
| int(CommonGizmosDataID::SupportsClipper));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::on_set_state()
|
||||
{
|
||||
// m_c->m_model_object pointer can be invalid (for instance because of undo/redo action),
|
||||
// we should recover it from the object id
|
||||
m_c->m_model_object = nullptr;
|
||||
for (const auto mo : wxGetApp().model().objects) {
|
||||
if (mo->id() == m_c->m_model_object_id) {
|
||||
m_c->m_model_object = mo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
if (m_state == m_old_state)
|
||||
return;
|
||||
|
|
@ -977,28 +892,16 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on")));
|
||||
|
||||
m_c->unstash_clipping_plane();
|
||||
update_clipping_plane(m_c->m_clipping_plane_was_moved);
|
||||
|
||||
m_c->build_AABB_if_needed();
|
||||
|
||||
|
||||
// we'll now reload support points:
|
||||
if (m_c->m_model_object)
|
||||
if (mo)
|
||||
reload_cache();
|
||||
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
if (m_c->m_model_object) {
|
||||
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
|
||||
m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance);
|
||||
}
|
||||
|
||||
// Set default head diameter from config.
|
||||
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
|
||||
}
|
||||
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
|
||||
bool will_ask = m_c->m_model_object && m_editing_mode && unsaved_changes();
|
||||
bool will_ask = mo && m_editing_mode && unsaved_changes();
|
||||
if (will_ask) {
|
||||
wxGetApp().CallAfter([this]() {
|
||||
// Following is called through CallAfter, because otherwise there was a problem
|
||||
|
|
@ -1017,16 +920,8 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
// we are actually shutting down
|
||||
disable_editing_mode(); // so it is not active next time the gizmo opens
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
m_normal_cache.clear();
|
||||
m_c->stash_clipping_plane();
|
||||
m_c->m_clipping_plane_distance = 0.f;
|
||||
update_clipping_plane(true);
|
||||
// Release clippers and the AABB raycaster.
|
||||
m_its = nullptr;
|
||||
m_c->m_object_clipper.reset();
|
||||
m_c->m_supports_clipper.reset();
|
||||
//m_c->m_mesh_raycaster.reset();
|
||||
|
||||
}
|
||||
}
|
||||
m_old_state = m_state;
|
||||
|
|
@ -1066,10 +961,7 @@ void GLGizmoSlaSupports::on_stop_dragging()
|
|||
|
||||
void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
|
||||
{
|
||||
ar(m_c->m_clipping_plane_distance,
|
||||
*m_c->m_clipping_plane,
|
||||
m_c->m_model_object_id,
|
||||
m_new_point_head_diameter,
|
||||
ar(m_new_point_head_diameter,
|
||||
m_normal_cache,
|
||||
m_editing_cache,
|
||||
m_selection_empty
|
||||
|
|
@ -1080,10 +972,7 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
|
|||
|
||||
void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const
|
||||
{
|
||||
ar(m_c->m_clipping_plane_distance,
|
||||
*m_c->m_clipping_plane,
|
||||
m_c->m_model_object_id,
|
||||
m_new_point_head_diameter,
|
||||
ar(m_new_point_head_diameter,
|
||||
m_normal_cache,
|
||||
m_editing_cache,
|
||||
m_selection_empty
|
||||
|
|
@ -1160,9 +1049,10 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
|
|||
for (const CacheEntry& ce : m_editing_cache)
|
||||
m_normal_cache.push_back(ce.support_point);
|
||||
|
||||
m_c->m_model_object->sla_points_status = sla::PointsStatus::UserModified;
|
||||
m_c->m_model_object->sla_support_points.clear();
|
||||
m_c->m_model_object->sla_support_points = m_normal_cache;
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
mo->sla_points_status = sla::PointsStatus::UserModified;
|
||||
mo->sla_support_points.clear();
|
||||
mo->sla_support_points = m_normal_cache;
|
||||
|
||||
reslice_SLA_supports();
|
||||
}
|
||||
|
|
@ -1172,23 +1062,25 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
|
|||
|
||||
void GLGizmoSlaSupports::reload_cache()
|
||||
{
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
m_normal_cache.clear();
|
||||
if (m_c->m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating)
|
||||
if (mo->sla_points_status == sla::PointsStatus::AutoGenerated || mo->sla_points_status == sla::PointsStatus::Generating)
|
||||
get_data_from_backend();
|
||||
else
|
||||
for (const sla::SupportPoint& point : m_c->m_model_object->sla_support_points)
|
||||
for (const sla::SupportPoint& point : mo->sla_support_points)
|
||||
m_normal_cache.emplace_back(point);
|
||||
}
|
||||
|
||||
|
||||
bool GLGizmoSlaSupports::has_backend_supports() const
|
||||
{
|
||||
if (! m_c->m_model_object)
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
if (! mo)
|
||||
return false;
|
||||
|
||||
// find SlaPrintObject with this ID
|
||||
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
|
||||
if (po->model_object()->id() == m_c->m_model_object->id())
|
||||
if (po->model_object()->id() == mo->id())
|
||||
return po->is_step_done(slaposSupportPoints);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -1196,24 +1088,28 @@ bool GLGizmoSlaSupports::has_backend_supports() const
|
|||
|
||||
void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) const
|
||||
{
|
||||
wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().plater()->reslice_SLA_supports(*m_c->m_model_object, postpone_error_messages); });
|
||||
wxGetApp().CallAfter([this, postpone_error_messages]() {
|
||||
wxGetApp().plater()->reslice_SLA_supports(
|
||||
*m_c->selection_info()->model_object(), postpone_error_messages);
|
||||
});
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::get_data_from_backend()
|
||||
{
|
||||
if (! has_backend_supports())
|
||||
return;
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
// find the respective SLAPrintObject, we need a pointer to it
|
||||
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
|
||||
if (po->model_object()->id() == m_c->m_model_object->id()) {
|
||||
if (po->model_object()->id() == mo->id()) {
|
||||
m_normal_cache.clear();
|
||||
const std::vector<sla::SupportPoint>& points = po->get_support_points();
|
||||
auto mat = po->trafo().inverse().cast<float>();
|
||||
for (unsigned int i=0; i<points.size();++i)
|
||||
m_normal_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island));
|
||||
|
||||
m_c->m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated;
|
||||
mo->sla_points_status = sla::PointsStatus::AutoGenerated;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1230,10 +1126,12 @@ void GLGizmoSlaSupports::auto_generate()
|
|||
_(L("Are you sure you want to do it?")) + "\n",
|
||||
_(L("Warning")), wxICON_WARNING | wxYES | wxNO);
|
||||
|
||||
if (m_c->m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) {
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) {
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points")));
|
||||
wxGetApp().CallAfter([this]() { reslice_SLA_supports(); });
|
||||
m_c->m_model_object->sla_points_status = sla::PointsStatus::Generating;
|
||||
mo->sla_points_status = sla::PointsStatus::Generating;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1248,7 +1146,7 @@ void GLGizmoSlaSupports::switch_to_editing_mode()
|
|||
m_editing_cache.emplace_back(sp);
|
||||
select_point(NoPoints);
|
||||
|
||||
m_parent.toggle_sla_auxiliaries_visibility(false, m_c->m_model_object, m_c->m_active_instance);
|
||||
m_c->instances_hider()->show_supports(false);
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
|
||||
|
|
@ -1258,7 +1156,7 @@ void GLGizmoSlaSupports::disable_editing_mode()
|
|||
if (m_editing_mode) {
|
||||
m_editing_mode = false;
|
||||
wxGetApp().plater()->leave_gizmos_stack();
|
||||
m_parent.toggle_sla_auxiliaries_visibility(true, m_c->m_model_object, m_c->m_active_instance);
|
||||
m_c->instances_hider()->show_supports(true);
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
|
|
@ -1277,20 +1175,6 @@ bool GLGizmoSlaSupports::unsaved_changes() const
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const
|
||||
{
|
||||
if (! m_c->m_model_object)
|
||||
return;
|
||||
Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ?
|
||||
m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward());
|
||||
|
||||
const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift);
|
||||
float dist = normal.dot(center);
|
||||
*m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius));
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
|
||||
SlaGizmoHelpDialog::SlaGizmoHelpDialog()
|
||||
: wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,33 +13,18 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class ClippingPlane;
|
||||
class MeshClipper;
|
||||
class MeshRaycaster;
|
||||
class CommonGizmosData;
|
||||
enum class SLAGizmoEventType : unsigned char;
|
||||
|
||||
class GLGizmoSlaSupports : public GLGizmoBase
|
||||
{
|
||||
private:
|
||||
//ModelObject* m_model_object = nullptr;
|
||||
//ObjectID m_model_object_id = 0;
|
||||
//int m_active_instance = -1;
|
||||
//float m_active_instance_bb_radius; // to cache the bb
|
||||
mutable double m_z_shift = 0.f;
|
||||
|
||||
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
|
||||
|
||||
const float RenderPointScale = 1.f;
|
||||
|
||||
GLUquadricObj* m_quadric;
|
||||
|
||||
//std::unique_ptr<MeshRaycaster> m_mesh_raycaster;
|
||||
//const TriangleMesh* m_mesh;
|
||||
const indexed_triangle_set* m_its;
|
||||
//mutable int m_old_timestamp = -1;
|
||||
//mutable int m_print_object_idx = -1;
|
||||
//mutable int m_print_objects_count = -1;
|
||||
|
||||
class CacheEntry {
|
||||
public:
|
||||
CacheEntry() :
|
||||
|
|
@ -73,14 +58,12 @@ public:
|
|||
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
void delete_selected_points(bool force = false);
|
||||
ClippingPlane get_sla_clipping_plane() const;
|
||||
//ClippingPlane get_sla_clipping_plane() const;
|
||||
|
||||
bool is_in_editing_mode() const { return m_editing_mode; }
|
||||
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); }
|
||||
bool has_backend_supports() const;
|
||||
void reslice_SLA_supports(bool postpone_error_messages = false) const;
|
||||
void update_clipping_plane(bool keep_normal = false) const;
|
||||
void set_common_data_ptr(CommonGizmosData* ptr) { m_c = ptr; }
|
||||
|
||||
private:
|
||||
bool on_init() override;
|
||||
|
|
@ -88,9 +71,7 @@ private:
|
|||
void on_render() const override;
|
||||
void on_render_for_picking() const override;
|
||||
|
||||
//void render_selection_rectangle() const;
|
||||
void render_points(const Selection& selection, bool picking = false) const;
|
||||
void render_clipping_plane(const Selection& selection) const;
|
||||
bool unsaved_changes() const;
|
||||
|
||||
bool m_lock_unique_islands = false;
|
||||
|
|
@ -102,8 +83,7 @@ private:
|
|||
float m_density_stash = 0.f; // and again
|
||||
mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selected
|
||||
std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
|
||||
|
||||
//std::unique_ptr<ClippingPlane> m_clipping_plane;
|
||||
const ModelObject* m_old_mo = nullptr;
|
||||
|
||||
// This map holds all translated description texts, so they can be easily referenced during layout calculations
|
||||
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
|
||||
|
|
@ -115,11 +95,6 @@ private:
|
|||
bool m_selection_empty = true;
|
||||
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
|
||||
|
||||
CommonGizmosData* m_c = nullptr;
|
||||
|
||||
//mutable std::unique_ptr<MeshClipper> m_object_clipper;
|
||||
//mutable std::unique_ptr<MeshClipper> m_supports_clipper;
|
||||
|
||||
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
bool is_mesh_point_clipped(const Vec3d& point) const;
|
||||
bool is_point_in_hole(const Vec3f& pt) const;
|
||||
|
|
@ -156,6 +131,7 @@ protected:
|
|||
std::string on_get_name() const override;
|
||||
bool on_is_activable() const override;
|
||||
bool on_is_selectable() const override;
|
||||
virtual CommonGizmosDataID on_get_requirements() const override;
|
||||
void on_load(cereal::BinaryInputArchive& ar) override;
|
||||
void on_save(cereal::BinaryOutputArchive& ar) const override;
|
||||
};
|
||||
|
|
|
|||
452
src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
Normal file
452
src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
#include "GLGizmosCommon.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
using namespace CommonGizmosDataObjects;
|
||||
|
||||
CommonGizmosDataPool::CommonGizmosDataPool(GLCanvas3D* canvas)
|
||||
: m_canvas(canvas)
|
||||
{
|
||||
using c = CommonGizmosDataID;
|
||||
m_data[c::SelectionInfo].reset( new SelectionInfo(this));
|
||||
m_data[c::InstancesHider].reset( new InstancesHider(this));
|
||||
m_data[c::HollowedMesh].reset( new HollowedMesh(this));
|
||||
m_data[c::Raycaster].reset( new Raycaster(this));
|
||||
m_data[c::ObjectClipper].reset( new ObjectClipper(this));
|
||||
m_data[c::SupportsClipper].reset( new SupportsClipper(this));
|
||||
|
||||
}
|
||||
|
||||
void CommonGizmosDataPool::update(CommonGizmosDataID required)
|
||||
{
|
||||
assert(check_dependencies(required));
|
||||
for (auto& [id, data] : m_data) {
|
||||
if (int(required) & int(CommonGizmosDataID(id)))
|
||||
data->update();
|
||||
else
|
||||
if (data->is_valid())
|
||||
data->release();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SelectionInfo* CommonGizmosDataPool::selection_info() const
|
||||
{
|
||||
SelectionInfo* sel_info = dynamic_cast<SelectionInfo*>(m_data.at(CommonGizmosDataID::SelectionInfo).get());
|
||||
assert(sel_info);
|
||||
return sel_info->is_valid() ? sel_info : nullptr;
|
||||
}
|
||||
|
||||
|
||||
InstancesHider* CommonGizmosDataPool::instances_hider() const
|
||||
{
|
||||
InstancesHider* inst_hider = dynamic_cast<InstancesHider*>(m_data.at(CommonGizmosDataID::InstancesHider).get());
|
||||
assert(inst_hider);
|
||||
return inst_hider->is_valid() ? inst_hider : nullptr;
|
||||
}
|
||||
|
||||
HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const
|
||||
{
|
||||
HollowedMesh* hol_mesh = dynamic_cast<HollowedMesh*>(m_data.at(CommonGizmosDataID::HollowedMesh).get());
|
||||
assert(hol_mesh);
|
||||
return hol_mesh->is_valid() ? hol_mesh : nullptr;
|
||||
}
|
||||
|
||||
Raycaster* CommonGizmosDataPool::raycaster() const
|
||||
{
|
||||
Raycaster* rc = dynamic_cast<Raycaster*>(m_data.at(CommonGizmosDataID::Raycaster).get());
|
||||
assert(rc);
|
||||
return rc->is_valid() ? rc : nullptr;
|
||||
}
|
||||
|
||||
ObjectClipper* CommonGizmosDataPool::object_clipper() const
|
||||
{
|
||||
ObjectClipper* oc = dynamic_cast<ObjectClipper*>(m_data.at(CommonGizmosDataID::ObjectClipper).get());
|
||||
assert(oc);
|
||||
return oc->is_valid() ? oc : nullptr;
|
||||
}
|
||||
|
||||
SupportsClipper* CommonGizmosDataPool::supports_clipper() const
|
||||
{
|
||||
SupportsClipper* sc = dynamic_cast<SupportsClipper*>(m_data.at(CommonGizmosDataID::SupportsClipper).get());
|
||||
assert(sc);
|
||||
return sc->is_valid() ? sc : nullptr;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check the required resources one by one and return true if all
|
||||
// dependencies are met.
|
||||
bool CommonGizmosDataPool::check_dependencies(CommonGizmosDataID required) const
|
||||
{
|
||||
// This should iterate over currently required data. Each of them should
|
||||
// be asked about its dependencies and it must check that all dependencies
|
||||
// are also in required and before the current one.
|
||||
for (auto& [id, data] : m_data) {
|
||||
// in case we don't use this, the deps are irrelevant
|
||||
if (! (int(required) & int(CommonGizmosDataID(id))))
|
||||
continue;
|
||||
|
||||
|
||||
CommonGizmosDataID deps = data->get_dependencies();
|
||||
assert(int(deps) == (int(deps) & int(required)));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
|
||||
|
||||
|
||||
void SelectionInfo::on_update()
|
||||
{
|
||||
const Selection& selection = get_pool()->get_canvas()->get_selection();
|
||||
if (selection.is_single_full_instance()) {
|
||||
m_model_object = selection.get_model()->objects[selection.get_object_idx()];
|
||||
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
|
||||
}
|
||||
else
|
||||
m_model_object = nullptr;
|
||||
}
|
||||
|
||||
void SelectionInfo::on_release()
|
||||
{
|
||||
m_model_object = nullptr;
|
||||
}
|
||||
|
||||
int SelectionInfo::get_active_instance() const
|
||||
{
|
||||
const Selection& selection = get_pool()->get_canvas()->get_selection();
|
||||
return selection.get_instance_idx();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void InstancesHider::on_update()
|
||||
{
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
int active_inst = get_pool()->selection_info()->get_active_instance();
|
||||
GLCanvas3D* canvas = get_pool()->get_canvas();
|
||||
|
||||
if (mo && active_inst != -1) {
|
||||
canvas->toggle_model_objects_visibility(false);
|
||||
canvas->toggle_model_objects_visibility(true, mo, active_inst);
|
||||
canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst);
|
||||
}
|
||||
else
|
||||
canvas->toggle_model_objects_visibility(true);
|
||||
}
|
||||
|
||||
void InstancesHider::on_release()
|
||||
{
|
||||
get_pool()->get_canvas()->toggle_model_objects_visibility(true);
|
||||
}
|
||||
|
||||
void InstancesHider::show_supports(bool show) {
|
||||
if (m_show_supports != show) {
|
||||
m_show_supports = show;
|
||||
on_update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void HollowedMesh::on_update()
|
||||
{
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
if (! mo)
|
||||
return;
|
||||
|
||||
const GLCanvas3D* canvas = get_pool()->get_canvas();
|
||||
const PrintObjects& print_objects = canvas->sla_print()->objects();
|
||||
const SLAPrintObject* print_object = m_print_object_idx != -1
|
||||
? print_objects[m_print_object_idx]
|
||||
: nullptr;
|
||||
|
||||
// Find the respective SLAPrintObject.
|
||||
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
|
||||
m_print_objects_count = print_objects.size();
|
||||
m_print_object_idx = -1;
|
||||
for (const SLAPrintObject* po : print_objects) {
|
||||
++m_print_object_idx;
|
||||
if (po->model_object()->id() == mo->id()) {
|
||||
print_object = po;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a valid SLAPrintObject, check state of Hollowing step.
|
||||
if (print_object) {
|
||||
if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) {
|
||||
size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp;
|
||||
if (timestamp > m_old_hollowing_timestamp) {
|
||||
const TriangleMesh& backend_mesh = print_object->get_mesh_to_print();
|
||||
if (! backend_mesh.empty()) {
|
||||
m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
|
||||
Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse();
|
||||
m_hollowed_mesh_transformed->transform(trafo_inv);
|
||||
m_old_hollowing_timestamp = timestamp;
|
||||
}
|
||||
else
|
||||
m_hollowed_mesh_transformed.reset(nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_hollowed_mesh_transformed.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HollowedMesh::on_release()
|
||||
{
|
||||
m_hollowed_mesh_transformed.reset();
|
||||
m_old_hollowing_timestamp = 0;
|
||||
m_print_object_idx = -1;
|
||||
}
|
||||
|
||||
|
||||
const TriangleMesh* HollowedMesh::get_hollowed_mesh() const
|
||||
{
|
||||
return m_hollowed_mesh_transformed.get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Raycaster::on_update()
|
||||
{
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
|
||||
if (! mo)
|
||||
return;
|
||||
|
||||
std::vector<const TriangleMesh*> meshes;
|
||||
const std::vector<ModelVolume*>& mvs = mo->volumes;
|
||||
if (mvs.size() == 1) {
|
||||
assert(mvs.front()->is_model_part());
|
||||
const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh();
|
||||
if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh())
|
||||
meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh());
|
||||
}
|
||||
if (meshes.empty()) {
|
||||
for (const ModelVolume* mv : mvs) {
|
||||
if (mv->is_model_part())
|
||||
meshes.push_back(&mv->mesh());
|
||||
}
|
||||
}
|
||||
|
||||
if (meshes != m_old_meshes) {
|
||||
m_raycasters.clear();
|
||||
for (const TriangleMesh* mesh : meshes)
|
||||
m_raycasters.emplace_back(new MeshRaycaster(*mesh));
|
||||
m_old_meshes = meshes;
|
||||
}
|
||||
}
|
||||
|
||||
void Raycaster::on_release()
|
||||
{
|
||||
m_raycasters.clear();
|
||||
m_old_meshes.clear();
|
||||
}
|
||||
|
||||
std::vector<const MeshRaycaster*> Raycaster::raycasters() const
|
||||
{
|
||||
std::vector<const MeshRaycaster*> mrcs;
|
||||
for (const auto& raycaster_unique_ptr : m_raycasters)
|
||||
mrcs.push_back(raycaster_unique_ptr.get());
|
||||
return mrcs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ObjectClipper::on_update()
|
||||
{
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
if (! mo)
|
||||
return;
|
||||
|
||||
// which mesh should be cut?
|
||||
const TriangleMesh* mesh = &mo->volumes.front()->mesh();
|
||||
bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh();
|
||||
if (has_hollowed)
|
||||
mesh = get_pool()->hollowed_mesh()->get_hollowed_mesh();
|
||||
|
||||
if (mesh != m_old_mesh) {
|
||||
m_clipper.reset(new MeshClipper);
|
||||
m_clipper->set_mesh(*mesh);
|
||||
m_old_mesh = mesh;
|
||||
m_active_inst_bb_radius =
|
||||
mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius();
|
||||
//if (has_hollowed && m_clp_ratio != 0.)
|
||||
// m_clp_ratio = 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ObjectClipper::on_release()
|
||||
{
|
||||
m_clipper.reset();
|
||||
m_old_mesh = nullptr;
|
||||
m_clp.reset();
|
||||
|
||||
}
|
||||
|
||||
void ObjectClipper::render_cut() const
|
||||
{
|
||||
if (m_clp_ratio == 0.)
|
||||
return;
|
||||
const SelectionInfo* sel_info = get_pool()->selection_info();
|
||||
const ModelObject* mo = sel_info->model_object();
|
||||
Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
|
||||
Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation();
|
||||
Geometry::Transformation trafo = inst_trafo * vol_trafo;
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
|
||||
|
||||
m_clipper->set_plane(*m_clp);
|
||||
m_clipper->set_transformation(trafo);
|
||||
|
||||
if (! m_clipper->get_triangles().empty()) {
|
||||
::glPushMatrix();
|
||||
::glColor3f(1.0f, 0.37f, 0.0f);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
for (const Vec3f& point : m_clipper->get_triangles())
|
||||
::glVertex3f(point(0), point(1), point(2));
|
||||
::glEnd();
|
||||
::glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ObjectClipper::set_position(double pos, bool keep_normal)
|
||||
{
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
int active_inst = get_pool()->selection_info()->get_active_instance();
|
||||
double z_shift = get_pool()->selection_info()->get_sla_shift();
|
||||
|
||||
Vec3d normal = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward();
|
||||
const Vec3d& center = mo->instances[active_inst]->get_offset() + Vec3d(0., 0., z_shift);
|
||||
float dist = normal.dot(center);
|
||||
|
||||
if (pos < 0.)
|
||||
pos = m_clp_ratio;
|
||||
|
||||
m_clp_ratio = pos;
|
||||
m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2*m_active_inst_bb_radius)));
|
||||
get_pool()->get_canvas()->set_as_dirty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SupportsClipper::on_update()
|
||||
{
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
if (! mo)
|
||||
return;
|
||||
|
||||
const GLCanvas3D* canvas = get_pool()->get_canvas();
|
||||
const PrintObjects& print_objects = canvas->sla_print()->objects();
|
||||
const SLAPrintObject* print_object = m_print_object_idx != -1
|
||||
? print_objects[m_print_object_idx]
|
||||
: nullptr;
|
||||
|
||||
// Find the respective SLAPrintObject.
|
||||
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
|
||||
m_print_objects_count = print_objects.size();
|
||||
m_print_object_idx = -1;
|
||||
for (const SLAPrintObject* po : print_objects) {
|
||||
++m_print_object_idx;
|
||||
if (po->model_object()->id() == mo->id()) {
|
||||
print_object = po;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (print_object
|
||||
&& print_object->is_step_done(slaposSupportTree)
|
||||
&& ! print_object->support_mesh().empty())
|
||||
{
|
||||
// If the supports are already calculated, save the timestamp of the respective step
|
||||
// so we can later tell they were recalculated.
|
||||
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
|
||||
if (! m_clipper || timestamp != m_old_timestamp) {
|
||||
// The timestamp has changed.
|
||||
m_clipper.reset(new MeshClipper);
|
||||
// The mesh should already have the shared vertices calculated.
|
||||
m_clipper->set_mesh(print_object->support_mesh());
|
||||
m_old_timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
else
|
||||
// The supports are not valid. We better dump the cached data.
|
||||
m_clipper.reset();
|
||||
}
|
||||
|
||||
|
||||
void SupportsClipper::on_release()
|
||||
{
|
||||
m_clipper.reset();
|
||||
m_old_timestamp = 0;
|
||||
m_print_object_idx = -1;
|
||||
}
|
||||
|
||||
void SupportsClipper::render_cut() const
|
||||
{
|
||||
const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper();
|
||||
if (ocl->get_position() == 0.
|
||||
|| ! get_pool()->instances_hider()->are_supports_shown()
|
||||
|| ! m_clipper)
|
||||
return;
|
||||
|
||||
const SelectionInfo* sel_info = get_pool()->selection_info();
|
||||
const ModelObject* mo = sel_info->model_object();
|
||||
Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
|
||||
//Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation();
|
||||
Geometry::Transformation trafo = inst_trafo;// * vol_trafo;
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
|
||||
|
||||
|
||||
// Get transformation of supports
|
||||
Geometry::Transformation supports_trafo = trafo;
|
||||
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift()));
|
||||
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
|
||||
// I don't know why, but following seems to be correct.
|
||||
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
|
||||
1,
|
||||
1.));
|
||||
|
||||
m_clipper->set_plane(*ocl->get_clipping_plane());
|
||||
m_clipper->set_transformation(supports_trafo);
|
||||
|
||||
if (! m_clipper->get_triangles().empty()) {
|
||||
::glPushMatrix();
|
||||
::glColor3f(1.0f, 0.f, 0.37f);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
for (const Vec3f& point : m_clipper->get_triangles())
|
||||
::glVertex3f(point(0), point(1), point(2));
|
||||
::glEnd();
|
||||
::glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
308
src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
Normal file
308
src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
#ifndef slic3r_GUI_GLGizmosCommon_hpp_
|
||||
#define slic3r_GUI_GLGizmosCommon_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
#include "slic3r/GUI/MeshUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ModelObject;
|
||||
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class GLCanvas3D;
|
||||
|
||||
static constexpr float HoleStickOutLength = 1.f;
|
||||
|
||||
enum class SLAGizmoEventType : unsigned char {
|
||||
LeftDown = 1,
|
||||
LeftUp,
|
||||
RightDown,
|
||||
Dragging,
|
||||
Delete,
|
||||
SelectAll,
|
||||
ShiftUp,
|
||||
AltUp,
|
||||
ApplyChanges,
|
||||
DiscardChanges,
|
||||
AutomaticGeneration,
|
||||
ManualEditing,
|
||||
MouseWheelUp,
|
||||
MouseWheelDown,
|
||||
ResetClippingPlane
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CommonGizmosDataBase;
|
||||
namespace CommonGizmosDataObjects {
|
||||
class SelectionInfo;
|
||||
class InstancesHider;
|
||||
class HollowedMesh;
|
||||
class Raycaster;
|
||||
class ObjectClipper;
|
||||
class SupportsClipper;
|
||||
}
|
||||
|
||||
// Some of the gizmos use the same data that need to be updated ocassionally.
|
||||
// It is also desirable that the data are not recalculated when the gizmos
|
||||
// are just switched, but on the other hand, they should be released when
|
||||
// they are not in use by any gizmo anymore.
|
||||
|
||||
// Enumeration of various data types that the data pool can contain.
|
||||
// Each gizmo can tell which of the data it wants to use through
|
||||
// on_get_requirements() method.
|
||||
enum class CommonGizmosDataID {
|
||||
None = 0,
|
||||
SelectionInfo = 1 << 0,
|
||||
InstancesHider = 1 << 1,
|
||||
HollowedMesh = 1 << 2,
|
||||
Raycaster = 1 << 3,
|
||||
ObjectClipper = 1 << 4,
|
||||
SupportsClipper = 1 << 5,
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Following class holds pointers to the common data objects and triggers
|
||||
// their updating/releasing. There is just one object of this type (managed
|
||||
// by GLGizmoManager, the gizmos keep a pointer to it.
|
||||
class CommonGizmosDataPool {
|
||||
public:
|
||||
CommonGizmosDataPool(GLCanvas3D* canvas);
|
||||
|
||||
// Update all resources and release what is not used.
|
||||
// Accepts a bitmask of currently required resources.
|
||||
void update(CommonGizmosDataID required);
|
||||
|
||||
// Getters for the data that need to be accessed from the gizmos directly.
|
||||
CommonGizmosDataObjects::SelectionInfo* selection_info() const;
|
||||
CommonGizmosDataObjects::InstancesHider* instances_hider() const;
|
||||
CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const;
|
||||
CommonGizmosDataObjects::Raycaster* raycaster() const;
|
||||
CommonGizmosDataObjects::ObjectClipper* object_clipper() const;
|
||||
CommonGizmosDataObjects::SupportsClipper* supports_clipper() const;
|
||||
|
||||
|
||||
GLCanvas3D* get_canvas() const { return m_canvas; }
|
||||
|
||||
private:
|
||||
std::map<CommonGizmosDataID, std::unique_ptr<CommonGizmosDataBase>> m_data;
|
||||
GLCanvas3D* m_canvas;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool check_dependencies(CommonGizmosDataID required) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Base class for a wrapper object managing a single resource.
|
||||
// Each of the enum values above (safe None) will have an object of this kind.
|
||||
class CommonGizmosDataBase {
|
||||
public:
|
||||
// Pass a backpointer to the pool, so the individual
|
||||
// objects can communicate with one another.
|
||||
explicit CommonGizmosDataBase(CommonGizmosDataPool* cgdp)
|
||||
: m_common{cgdp} {}
|
||||
virtual ~CommonGizmosDataBase() {}
|
||||
|
||||
// Update the resource.
|
||||
void update() { on_update(); m_is_valid = true; }
|
||||
|
||||
// Release any data that are stored internally.
|
||||
void release() { on_release(); m_is_valid = false; }
|
||||
|
||||
// Returns whether the resource is currently maintained.
|
||||
bool is_valid() const { return m_is_valid; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Return a bitmask of all resources that this one relies on.
|
||||
// The dependent resource must have higher ID than the one
|
||||
// it depends on.
|
||||
virtual CommonGizmosDataID get_dependencies() const { return CommonGizmosDataID::None; }
|
||||
#endif // NDEBUG
|
||||
|
||||
protected:
|
||||
virtual void on_release() = 0;
|
||||
virtual void on_update() = 0;
|
||||
CommonGizmosDataPool* get_pool() const { return m_common; }
|
||||
|
||||
|
||||
private:
|
||||
bool m_is_valid = false;
|
||||
CommonGizmosDataPool* m_common = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// The specializations of the CommonGizmosDataBase class live in this
|
||||
// namespace to avoid clashes in GUI namespace.
|
||||
namespace CommonGizmosDataObjects
|
||||
{
|
||||
|
||||
class SelectionInfo : public CommonGizmosDataBase
|
||||
{
|
||||
public:
|
||||
explicit SelectionInfo(CommonGizmosDataPool* cgdp)
|
||||
: CommonGizmosDataBase(cgdp) {}
|
||||
|
||||
ModelObject* model_object() const { return m_model_object; }
|
||||
int get_active_instance() const;
|
||||
float get_sla_shift() const { return m_z_shift; }
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
ModelObject* m_model_object = nullptr;
|
||||
int m_active_inst = -1;
|
||||
float m_z_shift = 0.f;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class InstancesHider : public CommonGizmosDataBase
|
||||
{
|
||||
public:
|
||||
explicit InstancesHider(CommonGizmosDataPool* cgdp)
|
||||
: CommonGizmosDataBase(cgdp) {}
|
||||
#ifndef NDEBUG
|
||||
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
|
||||
#endif // NDEBUG
|
||||
|
||||
void show_supports(bool show);
|
||||
bool are_supports_shown() const { return m_show_supports; }
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
bool m_show_supports = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class HollowedMesh : public CommonGizmosDataBase
|
||||
{
|
||||
public:
|
||||
explicit HollowedMesh(CommonGizmosDataPool* cgdp)
|
||||
: CommonGizmosDataBase(cgdp) {}
|
||||
#ifndef NDEBUG
|
||||
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
|
||||
#endif // NDEBUG
|
||||
|
||||
const TriangleMesh* get_hollowed_mesh() const;
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<TriangleMesh> m_hollowed_mesh_transformed;
|
||||
size_t m_old_hollowing_timestamp = 0;
|
||||
int m_print_object_idx = -1;
|
||||
int m_print_objects_count = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Raycaster : public CommonGizmosDataBase
|
||||
{
|
||||
public:
|
||||
explicit Raycaster(CommonGizmosDataPool* cgdp)
|
||||
: CommonGizmosDataBase(cgdp) {}
|
||||
#ifndef NDEBUG
|
||||
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
|
||||
#endif // NDEBUG
|
||||
|
||||
const MeshRaycaster* raycaster() const { assert(m_raycasters.size() == 1); return m_raycasters.front().get(); }
|
||||
std::vector<const MeshRaycaster*> raycasters() const;
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<MeshRaycaster>> m_raycasters;
|
||||
std::vector<const TriangleMesh*> m_old_meshes;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ObjectClipper : public CommonGizmosDataBase
|
||||
{
|
||||
public:
|
||||
explicit ObjectClipper(CommonGizmosDataPool* cgdp)
|
||||
: CommonGizmosDataBase(cgdp) {}
|
||||
#ifndef NDEBUG
|
||||
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
|
||||
#endif // NDEBUG
|
||||
|
||||
void set_position(double pos, bool keep_normal);
|
||||
double get_position() const { return m_clp_ratio; }
|
||||
ClippingPlane* get_clipping_plane() const { return m_clp.get(); }
|
||||
void render_cut() const;
|
||||
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
const TriangleMesh* m_old_mesh = nullptr;
|
||||
std::unique_ptr<MeshClipper> m_clipper;
|
||||
std::unique_ptr<ClippingPlane> m_clp;
|
||||
double m_clp_ratio = 0.;
|
||||
double m_active_inst_bb_radius = 0.;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class SupportsClipper : public CommonGizmosDataBase
|
||||
{
|
||||
public:
|
||||
explicit SupportsClipper(CommonGizmosDataPool* cgdp)
|
||||
: CommonGizmosDataBase(cgdp) {}
|
||||
#ifndef NDEBUG
|
||||
CommonGizmosDataID get_dependencies() const override {
|
||||
return CommonGizmosDataID(
|
||||
int(CommonGizmosDataID::SelectionInfo)
|
||||
| int(CommonGizmosDataID::ObjectClipper)
|
||||
);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
void render_cut() const;
|
||||
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
size_t m_old_timestamp = 0;
|
||||
int m_print_object_idx = -1;
|
||||
int m_print_objects_count = 0;
|
||||
std::unique_ptr<MeshClipper> m_clipper;
|
||||
};
|
||||
|
||||
} // namespace CommonGizmosDataObjects
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
#endif // slic3r_GUI_GLGizmosCommon_hpp_
|
||||
|
|
@ -2,14 +2,21 @@
|
|||
#include "GLGizmosManager.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
|
||||
#include "slic3r/GUI/PresetBundle.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "slic3r/GUI/MeshUtils.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMove.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoScale.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoCut.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"
|
||||
|
||||
#include <wx/glcanvas.h>
|
||||
|
||||
|
|
@ -96,9 +103,11 @@ bool GLGizmosManager::init()
|
|||
m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6));
|
||||
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "sla_supports.svg", 7));
|
||||
|
||||
m_common_gizmos_data.reset(new CommonGizmosData());
|
||||
dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->set_common_data_ptr(m_common_gizmos_data.get());
|
||||
dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->set_common_data_ptr(m_common_gizmos_data.get());
|
||||
//m_common_gizmos_data.reset(new CommonGizmosData());
|
||||
//dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->set_common_data_ptr(m_common_gizmos_data.get());
|
||||
//dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->set_common_data_ptr(m_common_gizmos_data.get());
|
||||
|
||||
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
|
||||
|
||||
|
||||
for (auto& gizmo : m_gizmos) {
|
||||
|
|
@ -106,6 +115,7 @@ bool GLGizmosManager::init()
|
|||
m_gizmos.clear();
|
||||
return false;
|
||||
}
|
||||
gizmo->set_common_data_pool(m_common_gizmos_data.get());
|
||||
}
|
||||
|
||||
m_current = Undefined;
|
||||
|
|
@ -197,6 +207,10 @@ void GLGizmosManager::update_data()
|
|||
enable_grabber(Scale, i, enable_scale_xyz);
|
||||
}
|
||||
|
||||
m_common_gizmos_data->update(get_current()
|
||||
? get_current()->get_requirements()
|
||||
: CommonGizmosDataID(0));
|
||||
|
||||
if (selection.is_single_full_instance())
|
||||
{
|
||||
// all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
|
||||
|
|
@ -231,8 +245,8 @@ void GLGizmosManager::update_data()
|
|||
set_scale(Vec3d::Ones());
|
||||
set_rotation(Vec3d::Zero());
|
||||
set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
|
||||
set_sla_support_data(nullptr);
|
||||
set_fdm_support_data(nullptr);
|
||||
set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
|
||||
set_fdm_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -361,15 +375,19 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object)
|
|||
|| wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
|
||||
return;
|
||||
|
||||
m_common_gizmos_data->update_from_backend(m_parent, model_object);
|
||||
/*m_common_gizmos_data->update_from_backend(m_parent, model_object);
|
||||
|
||||
auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get());
|
||||
auto* gizmo_hollow = dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get());
|
||||
|
||||
|
||||
// note: sla support gizmo takes care of updating the common data.
|
||||
// following lines are thus dependent
|
||||
gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection());
|
||||
//gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection());
|
||||
*/
|
||||
auto* gizmo_hollow = dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get());
|
||||
auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get());
|
||||
gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection());
|
||||
gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection());
|
||||
}
|
||||
|
||||
void GLGizmosManager::set_fdm_support_data(ModelObject* model_object)
|
||||
|
|
@ -398,15 +416,13 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
|
|||
|
||||
ClippingPlane GLGizmosManager::get_clipping_plane() const
|
||||
{
|
||||
if (!m_enabled || (m_current != SlaSupports && m_current != Hollow && m_current != FdmSupports) || m_gizmos.empty())
|
||||
if (! m_common_gizmos_data->object_clipper()
|
||||
|| m_common_gizmos_data->object_clipper()->get_position() == 0.)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
|
||||
if (m_current == SlaSupports)
|
||||
return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->get_sla_clipping_plane();
|
||||
else if (m_current == Hollow)
|
||||
return dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->get_sla_clipping_plane();
|
||||
else
|
||||
return dynamic_cast<GLGizmoFdmSupports*>(m_gizmos[FdmSupports].get())->get_fdm_clipping_plane();
|
||||
else {
|
||||
const ClippingPlane& clp = *m_common_gizmos_data->object_clipper()->get_clipping_plane();
|
||||
return ClippingPlane(-clp.get_normal(), clp.get_data()[3]);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmosManager::wants_reslice_supports_on_undo() const
|
||||
|
|
@ -443,6 +459,15 @@ void GLGizmosManager::render_overlay() const
|
|||
do_render_overlay();
|
||||
}
|
||||
|
||||
std::string GLGizmosManager::get_tooltip() const
|
||||
{
|
||||
if (!m_tooltip.empty())
|
||||
return m_tooltip;
|
||||
|
||||
const GLGizmoBase* curr = get_current();
|
||||
return (curr != nullptr) ? curr->get_tooltip() : "";
|
||||
}
|
||||
|
||||
bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt)
|
||||
{
|
||||
bool processed = false;
|
||||
|
|
@ -468,6 +493,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
int selected_object_idx = selection.get_object_idx();
|
||||
bool processed = false;
|
||||
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
// mouse anywhere
|
||||
if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr))
|
||||
{
|
||||
|
|
@ -477,10 +503,81 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
|
||||
m_mouse_capture.reset();
|
||||
}
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
|
||||
// mouse anywhere
|
||||
if (evt.Moving())
|
||||
m_tooltip = update_hover_state(mouse_pos);
|
||||
#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
else if (evt.LeftUp())
|
||||
{
|
||||
if (m_mouse_capture.left)
|
||||
{
|
||||
processed = true;
|
||||
m_mouse_capture.left = false;
|
||||
}
|
||||
else if (is_dragging())
|
||||
{
|
||||
switch (m_current) {
|
||||
case Move: m_parent.do_move(L("Gizmo-Move")); break;
|
||||
case Scale: m_parent.do_scale(L("Gizmo-Scale")); break;
|
||||
case Rotate: m_parent.do_rotate(L("Gizmo-Rotate")); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
stop_dragging();
|
||||
update_data();
|
||||
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
// Let the plater know that the dragging finished, so a delayed refresh
|
||||
// of the scene with the background processing data should be performed.
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
|
||||
// updates camera target constraints
|
||||
m_parent.refresh_camera_scene_box();
|
||||
|
||||
processed = true;
|
||||
}
|
||||
// else
|
||||
// return false;
|
||||
}
|
||||
else if (evt.MiddleUp())
|
||||
{
|
||||
if (m_mouse_capture.middle)
|
||||
{
|
||||
processed = true;
|
||||
m_mouse_capture.middle = false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (evt.RightUp())
|
||||
{
|
||||
if (pending_right_up)
|
||||
{
|
||||
pending_right_up = false;
|
||||
return true;
|
||||
}
|
||||
if (m_mouse_capture.right)
|
||||
{
|
||||
processed = true;
|
||||
m_mouse_capture.right = false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
#if ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX
|
||||
else if (evt.Dragging() && !is_dragging())
|
||||
#else
|
||||
else if (evt.Dragging())
|
||||
#endif // ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX
|
||||
{
|
||||
if (m_mouse_capture.any())
|
||||
// if the button down was done on this toolbar, prevent from dragging into the scene
|
||||
processed = true;
|
||||
// else
|
||||
// return false;
|
||||
}
|
||||
#else
|
||||
else if (evt.LeftUp())
|
||||
m_mouse_capture.left = false;
|
||||
else if (evt.MiddleUp())
|
||||
|
|
@ -497,6 +594,55 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
else if (evt.Dragging() && m_mouse_capture.any())
|
||||
// if the button down was done on this toolbar, prevent from dragging into the scene
|
||||
processed = true;
|
||||
#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
#if ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX
|
||||
else if (evt.Dragging() && is_dragging())
|
||||
{
|
||||
if (!m_parent.get_wxglcanvas()->HasCapture())
|
||||
m_parent.get_wxglcanvas()->CaptureMouse();
|
||||
|
||||
m_parent.set_mouse_as_dragging();
|
||||
update(m_parent.mouse_ray(pos), pos);
|
||||
|
||||
switch (m_current)
|
||||
{
|
||||
case Move:
|
||||
{
|
||||
// Apply new temporary offset
|
||||
selection.translate(get_displacement());
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
break;
|
||||
}
|
||||
case Scale:
|
||||
{
|
||||
// Apply new temporary scale factors
|
||||
TransformationType transformation_type(TransformationType::Local_Absolute_Joint);
|
||||
if (evt.AltDown())
|
||||
transformation_type.set_independent();
|
||||
selection.scale(get_scale(), transformation_type);
|
||||
if (evt.ControlDown())
|
||||
selection.translate(get_scale_offset(), true);
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
break;
|
||||
}
|
||||
case Rotate:
|
||||
{
|
||||
// Apply new temporary rotations
|
||||
TransformationType transformation_type(TransformationType::World_Relative_Joint);
|
||||
if (evt.AltDown())
|
||||
transformation_type.set_independent();
|
||||
selection.rotate(get_rotation(), transformation_type);
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_parent.set_as_dirty();
|
||||
processed = true;
|
||||
}
|
||||
#endif // ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX
|
||||
|
||||
if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined)
|
||||
{
|
||||
|
|
@ -541,6 +687,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
m_parent.set_as_dirty();
|
||||
processed = true;
|
||||
}
|
||||
#if !ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX
|
||||
else if (evt.Dragging() && is_dragging())
|
||||
{
|
||||
if (!m_parent.get_wxglcanvas()->HasCapture())
|
||||
|
|
@ -587,6 +734,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
m_parent.set_as_dirty();
|
||||
processed = true;
|
||||
}
|
||||
#endif // !ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
else if (evt.LeftUp() && is_dragging())
|
||||
{
|
||||
switch (m_current) {
|
||||
|
|
@ -608,6 +757,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
|
||||
processed = true;
|
||||
}
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && !m_parent.is_mouse_dragging())
|
||||
{
|
||||
// in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither
|
||||
|
|
@ -646,8 +796,10 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
m_mouse_capture.right = true;
|
||||
m_mouse_capture.parent = &m_parent;
|
||||
}
|
||||
#if !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
else if (evt.LeftUp())
|
||||
processed = true;
|
||||
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
|
||||
}
|
||||
|
||||
return processed;
|
||||
|
|
@ -915,8 +1067,13 @@ void GLGizmosManager::do_render_overlay() const
|
|||
|
||||
float cnv_w = (float)m_parent.get_canvas_size().get_width();
|
||||
float cnv_h = (float)m_parent.get_canvas_size().get_height();
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float zoom = (float)wxGetApp().plater()->get_camera().get_zoom();
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float zoom = (float)m_parent.get_camera().get_zoom();
|
||||
float inv_zoom = (float)m_parent.get_camera().get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
float height = get_scaled_total_height();
|
||||
float width = get_scaled_total_width();
|
||||
|
|
@ -967,7 +1124,11 @@ void GLGizmosManager::do_render_overlay() const
|
|||
|
||||
GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
|
||||
if (idx == m_current) {
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height();
|
||||
#else
|
||||
float toolbar_top = cnv_h - m_parent.get_view_toolbar_height();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top);
|
||||
}
|
||||
zoomed_top_y -= zoomed_stride_y;
|
||||
|
|
@ -1062,10 +1223,13 @@ void GLGizmosManager::activate_gizmo(EType type)
|
|||
return; // gizmo refused to be turned off, do nothing.
|
||||
}
|
||||
|
||||
m_current = type;
|
||||
m_common_gizmos_data->update(get_current()
|
||||
? get_current()->get_requirements()
|
||||
: CommonGizmosDataID(0));
|
||||
|
||||
if (type != Undefined)
|
||||
m_gizmos[type]->set_state(GLGizmoBase::On);
|
||||
|
||||
m_current = type;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1078,135 +1242,5 @@ bool GLGizmosManager::grabber_contains_mouse() const
|
|||
return (curr != nullptr) ? (curr->get_hover_id() != -1) : false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CommonGizmosData::CommonGizmosData()
|
||||
{
|
||||
m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* model_object)
|
||||
{
|
||||
recent_update = false;
|
||||
bool object_changed = false;
|
||||
|
||||
if (m_model_object != model_object
|
||||
|| (model_object && m_model_object_id != model_object->id())) {
|
||||
m_model_object = model_object;
|
||||
m_print_object_idx = -1;
|
||||
m_mesh_raycaster.reset();
|
||||
m_object_clipper.reset();
|
||||
m_supports_clipper.reset();
|
||||
m_old_mesh = nullptr;
|
||||
m_mesh = nullptr;
|
||||
m_backend_mesh_transformed.clear();
|
||||
|
||||
object_changed = true;
|
||||
recent_update = true;
|
||||
}
|
||||
|
||||
if (m_model_object) {
|
||||
int active_inst = canvas.get_selection().get_instance_idx();
|
||||
if (m_active_instance != active_inst) {
|
||||
m_active_instance = active_inst;
|
||||
m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius();
|
||||
recent_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (! m_model_object || ! canvas.get_selection().is_from_single_instance())
|
||||
return false;
|
||||
|
||||
int old_po_idx = m_print_object_idx;
|
||||
|
||||
// First we need a pointer to the respective SLAPrintObject. The index into objects vector is
|
||||
// cached so we don't have todo it on each render. We only search for the po if needed:
|
||||
if (m_print_object_idx < 0 || (int)canvas.sla_print()->objects().size() != m_print_objects_count) {
|
||||
m_print_objects_count = canvas.sla_print()->objects().size();
|
||||
m_print_object_idx = -1;
|
||||
for (const SLAPrintObject* po : canvas.sla_print()->objects()) {
|
||||
++m_print_object_idx;
|
||||
if (po->model_object()->id() == m_model_object->id())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool mesh_exchanged = false;
|
||||
m_mesh = nullptr;
|
||||
// Load either the model_object mesh, or one provided by the backend
|
||||
// This mesh does not account for the possible Z up SLA offset.
|
||||
// The backend mesh needs to be transformed and because a pointer to it is
|
||||
// saved, a copy is stored as a member (FIXME)
|
||||
if (m_print_object_idx >=0) {
|
||||
const SLAPrintObject* po = canvas.sla_print()->objects()[m_print_object_idx];
|
||||
if (po->is_step_done(slaposDrillHoles)) {
|
||||
m_backend_mesh_transformed = po->get_mesh_to_print();
|
||||
m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse());
|
||||
m_mesh = &m_backend_mesh_transformed;
|
||||
m_has_drilled_mesh = true;
|
||||
mesh_exchanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_mesh) {
|
||||
m_mesh = &m_model_object->volumes.front()->mesh();
|
||||
m_backend_mesh_transformed.clear();
|
||||
m_has_drilled_mesh = false;
|
||||
}
|
||||
|
||||
m_model_object_id = m_model_object->id();
|
||||
|
||||
if (m_mesh != m_old_mesh) {
|
||||
// Update clipping plane position.
|
||||
float new_clp_pos = m_clipping_plane_distance;
|
||||
if (object_changed) {
|
||||
new_clp_pos = 0.f;
|
||||
m_clipping_plane_was_moved = false;
|
||||
} else {
|
||||
// After we got a drilled mesh, move the cp to 25%. This only applies when
|
||||
// the hollowing gizmo is active and hollowing is enabled
|
||||
if (m_clipping_plane_distance == 0.f && mesh_exchanged && m_has_drilled_mesh) {
|
||||
const DynamicPrintConfig& cfg =
|
||||
(m_model_object && m_model_object->config.has("hollowing_enable"))
|
||||
? m_model_object->config
|
||||
: wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
|
||||
if (cfg.has("hollowing_enable") && cfg.opt_bool("hollowing_enable")
|
||||
&& canvas.get_gizmos_manager().get_current_type() == GLGizmosManager::Hollow) {
|
||||
new_clp_pos = 0.25f;
|
||||
m_clipping_plane_was_moved = false; // so it uses current camera direction
|
||||
}
|
||||
}
|
||||
}
|
||||
m_clipping_plane_distance = new_clp_pos;
|
||||
m_clipping_plane_distance_stash = new_clp_pos;
|
||||
|
||||
m_schedule_aabb_calculation = true;
|
||||
recent_update = true;
|
||||
return true;
|
||||
}
|
||||
if (! recent_update)
|
||||
recent_update = m_print_object_idx < 0 && old_po_idx >= 0;
|
||||
|
||||
return recent_update;
|
||||
}
|
||||
|
||||
|
||||
void CommonGizmosData::build_AABB_if_needed()
|
||||
{
|
||||
if (! m_schedule_aabb_calculation)
|
||||
return;
|
||||
|
||||
wxBusyCursor wait;
|
||||
m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh));
|
||||
m_object_clipper.reset();
|
||||
m_supports_clipper.reset();
|
||||
m_old_mesh = m_mesh;
|
||||
m_schedule_aabb_calculation = false;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#include "slic3r/GUI/GLTexture.hpp"
|
||||
#include "slic3r/GUI/GLToolbar.hpp"
|
||||
#include "libslic3r/ObjectID.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoBase.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ namespace GUI {
|
|||
class GLCanvas3D;
|
||||
class ClippingPlane;
|
||||
enum class SLAGizmoEventType : unsigned char;
|
||||
class CommonGizmosData;
|
||||
class CommonGizmosDataPool;
|
||||
|
||||
class Rect
|
||||
{
|
||||
|
|
@ -116,7 +116,8 @@ private:
|
|||
MouseCapture m_mouse_capture;
|
||||
std::string m_tooltip;
|
||||
bool m_serializing;
|
||||
std::unique_ptr<CommonGizmosData> m_common_gizmos_data;
|
||||
//std::unique_ptr<CommonGizmosData> m_common_gizmos_data;
|
||||
std::unique_ptr<CommonGizmosDataPool> m_common_gizmos_data;
|
||||
|
||||
public:
|
||||
explicit GLGizmosManager(GLCanvas3D& parent);
|
||||
|
|
@ -210,7 +211,7 @@ public:
|
|||
|
||||
void render_overlay() const;
|
||||
|
||||
const std::string& get_tooltip() const { return m_tooltip; }
|
||||
std::string get_tooltip() const;
|
||||
|
||||
bool on_mouse(wxMouseEvent& evt);
|
||||
bool on_mouse_wheel(wxMouseEvent& evt);
|
||||
|
|
@ -235,63 +236,6 @@ private:
|
|||
|
||||
|
||||
|
||||
class MeshRaycaster;
|
||||
class MeshClipper;
|
||||
|
||||
// This class is only for sharing SLA related data between SLA gizmos
|
||||
// and its synchronization with backend data. It should not be misused
|
||||
// for anything else.
|
||||
class CommonGizmosData {
|
||||
public:
|
||||
CommonGizmosData();
|
||||
const TriangleMesh* mesh() const {
|
||||
return (! m_mesh ? nullptr : m_mesh); //(m_cavity_mesh ? m_cavity_mesh.get() : m_mesh));
|
||||
}
|
||||
|
||||
bool update_from_backend(GLCanvas3D& canvas, ModelObject* model_object);
|
||||
bool recent_update = false;
|
||||
static constexpr float HoleStickOutLength = 1.f;
|
||||
|
||||
ModelObject* m_model_object = nullptr;
|
||||
const TriangleMesh* m_mesh;
|
||||
std::unique_ptr<MeshRaycaster> m_mesh_raycaster;
|
||||
std::unique_ptr<MeshClipper> m_object_clipper;
|
||||
std::unique_ptr<MeshClipper> m_supports_clipper;
|
||||
|
||||
//std::unique_ptr<TriangleMesh> m_cavity_mesh;
|
||||
//std::unique_ptr<GLVolume> m_volume_with_cavity;
|
||||
|
||||
int m_active_instance = -1;
|
||||
float m_active_instance_bb_radius = 0;
|
||||
ObjectID m_model_object_id = 0;
|
||||
int m_print_object_idx = -1;
|
||||
int m_print_objects_count = -1;
|
||||
int m_old_timestamp = -1;
|
||||
|
||||
float m_clipping_plane_distance = 0.f;
|
||||
std::unique_ptr<ClippingPlane> m_clipping_plane;
|
||||
bool m_clipping_plane_was_moved = false;
|
||||
|
||||
void stash_clipping_plane() {
|
||||
m_clipping_plane_distance_stash = m_clipping_plane_distance;
|
||||
}
|
||||
|
||||
void unstash_clipping_plane() {
|
||||
m_clipping_plane_distance = m_clipping_plane_distance_stash;
|
||||
}
|
||||
|
||||
bool has_drilled_mesh() const { return m_has_drilled_mesh; }
|
||||
|
||||
void build_AABB_if_needed();
|
||||
|
||||
private:
|
||||
const TriangleMesh* m_old_mesh;
|
||||
TriangleMesh m_backend_mesh_transformed;
|
||||
float m_clipping_plane_distance_stash = 0.f;
|
||||
bool m_has_drilled_mesh = false;
|
||||
bool m_schedule_aabb_calculation = false;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#ifndef _
|
||||
#define _(s) Slic3r::GUI::I18N::translate((s))
|
||||
#define _(s) Slic3r::GUI::I18N::translate((s))
|
||||
#define _L(s) Slic3r::GUI::I18N::translate((s))
|
||||
#define _utf8(s) Slic3r::GUI::I18N::translate_utf8((s))
|
||||
#define _u8L(s) Slic3r::GUI::I18N::translate_utf8((s))
|
||||
#endif /* _ */
|
||||
|
||||
#ifndef _CTX
|
||||
#define _CTX(s, ctx) Slic3r::GUI::I18N::translate((s), (ctx))
|
||||
#define _CTX(s, ctx) Slic3r::GUI::I18N::translate((s), (ctx))
|
||||
#define _CTX_utf8(s, ctx) Slic3r::GUI::I18N::translate_utf8((s), (ctx))
|
||||
#endif /* _ */
|
||||
|
||||
|
|
|
|||
|
|
@ -441,15 +441,37 @@ bool ImGuiWrapper::want_any_input() const
|
|||
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static const ImWchar ranges_keyboard_shortcuts[] =
|
||||
{
|
||||
0x21E7, 0x21E7, // OSX Shift Key symbol
|
||||
0x2318, 0x2318, // OSX Command Key symbol
|
||||
0x2325, 0x2325, // OSX Option Key symbol
|
||||
0,
|
||||
};
|
||||
#endif // __APPLE__
|
||||
|
||||
void ImGuiWrapper::init_font(bool compress)
|
||||
{
|
||||
destroy_font();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->Clear();
|
||||
//FIXME replace with io.Fonts->AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, m_font_size, nullptr, m_glyph_ranges);
|
||||
|
||||
// Create ranges of characters from m_glyph_ranges, possibly adding some OS specific special characters.
|
||||
ImVector<ImWchar> ranges;
|
||||
ImFontAtlas::GlyphRangesBuilder builder;
|
||||
builder.AddRanges(m_glyph_ranges);
|
||||
#ifdef __APPLE__
|
||||
if (m_font_cjk)
|
||||
// Apple keyboard shortcuts are only contained in the CJK fonts.
|
||||
builder.AddRanges(ranges_keyboard_shortcuts);
|
||||
#endif
|
||||
builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
|
||||
|
||||
//FIXME replace with io.Fonts->AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, m_font_size, nullptr, ranges.Data);
|
||||
//https://github.com/ocornut/imgui/issues/220
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + (m_font_cjk ? "NotoSansCJK-Regular.ttc" : "NotoSans-Regular.ttf")).c_str(), m_font_size, nullptr, m_glyph_ranges);
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + (m_font_cjk ? "NotoSansCJK-Regular.ttc" : "NotoSans-Regular.ttf")).c_str(), m_font_size, nullptr, ranges.Data);
|
||||
if (font == nullptr) {
|
||||
font = io.Fonts->AddFontDefault();
|
||||
if (font == nullptr) {
|
||||
|
|
@ -457,6 +479,16 @@ void ImGuiWrapper::init_font(bool compress)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
ImFontConfig config;
|
||||
config.MergeMode = true;
|
||||
if (! m_font_cjk) {
|
||||
// Apple keyboard shortcuts are only contained in the CJK fonts.
|
||||
ImFont *font_cjk = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSansCJK-Regular.ttc").c_str(), m_font_size, &config, ranges_keyboard_shortcuts);
|
||||
assert(font_cjk != nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Build texture atlas
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
{ ctrl + "G", L("Export G-code") },
|
||||
{ ctrl + "Shift+" + "G", L("Send G-code") },
|
||||
{ ctrl + "E", L("Export config") },
|
||||
{ ctrl + "U", L("Export to SD card / Flash drive") },
|
||||
{ ctrl + "T", L("Eject SD card / Flash drive") },
|
||||
// Edit
|
||||
{ ctrl + "A", L("Select all objects") },
|
||||
{ "Esc", L("Deselect all") },
|
||||
|
|
@ -142,9 +144,10 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
{ ctrl + "J", L("Print host upload queue") },
|
||||
// View
|
||||
{ "0-6", L("Camera view") },
|
||||
#if ENABLE_SHOW_SCENE_LABELS
|
||||
{ "E", L("Show/Hide object/instance labels") },
|
||||
#endif // ENABLE_SHOW_SCENE_LABELS
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
{ "D", L("Turn On/Off facets' slope rendering") },
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
// Configuration
|
||||
{ ctrl + "P", L("Preferences") },
|
||||
// Help
|
||||
|
|
@ -182,10 +185,12 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
{ "Z", L("Zoom to selected object\nor all objects in scene, if none selected") },
|
||||
{ "I", L("Zoom in") },
|
||||
{ "O", L("Zoom out") },
|
||||
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") }
|
||||
#ifdef __linux__
|
||||
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") },
|
||||
#endif // __linux__
|
||||
#if ENABLE_RENDER_PICKING_PASS
|
||||
// Don't localize debugging texts.
|
||||
, { "T", "Toggle picking pass texture rendering on/off" }
|
||||
{ "T", "Toggle picking pass texture rendering on/off" },
|
||||
#endif // ENABLE_RENDER_PICKING_PASS
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef slic3r_LambdaObjectDialog_hpp_
|
||||
#define slic3r_LambdaObjectDialog_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/sizer.h>
|
||||
|
|
|
|||
|
|
@ -25,11 +25,17 @@
|
|||
#include "wxExtensions.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "Mouse3DController.hpp"
|
||||
#include "RemovableDriveManager.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <dbt.h>
|
||||
#include <shlobj.h>
|
||||
#endif // _WIN32
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
|
@ -103,33 +109,71 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
update_title();
|
||||
|
||||
// declare events
|
||||
Bind(wxEVT_CREATE, [this](wxWindowCreateEvent& event) {
|
||||
|
||||
#ifdef _WIN32
|
||||
//static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };
|
||||
//static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b };
|
||||
//static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f };
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
|
||||
|
||||
// Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration.
|
||||
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 };
|
||||
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
||||
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID;
|
||||
m_hDeviceNotify = ::RegisterDeviceNotification(this->GetHWND(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
// or register for file handle change?
|
||||
// DEV_BROADCAST_HANDLE NotificationFilter = { 0 };
|
||||
// NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
||||
// NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
||||
|
||||
// Using Win32 Shell API to register for media insert / removal events.
|
||||
LPITEMIDLIST ppidl;
|
||||
if (SHGetSpecialFolderLocation(this->GetHWND(), CSIDL_DESKTOP, &ppidl) == NOERROR) {
|
||||
SHChangeNotifyEntry shCNE;
|
||||
shCNE.pidl = ppidl;
|
||||
shCNE.fRecursive = TRUE;
|
||||
// Returns a positive integer registration identifier (ID).
|
||||
// Returns zero if out of memory or in response to invalid parameters.
|
||||
m_ulSHChangeNotifyRegister = SHChangeNotifyRegister(this->GetHWND(), // Hwnd to receive notification
|
||||
SHCNE_DISKEVENTS, // Event types of interest (sources)
|
||||
SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED,
|
||||
//SHCNE_UPDATEITEM, // Events of interest - use SHCNE_ALLEVENTS for all events
|
||||
WM_USER_MEDIACHANGED, // Notification message to be sent upon the event
|
||||
1, // Number of entries in the pfsne array
|
||||
&shCNE); // Array of SHChangeNotifyEntry structures that
|
||||
// contain the notifications. This array should
|
||||
// always be set to one when calling SHChnageNotifyRegister
|
||||
// or SHChangeNotifyDeregister will not work properly.
|
||||
assert(m_ulSHChangeNotifyRegister != 0); // Shell notification failed
|
||||
} else {
|
||||
// Failed to get desktop location
|
||||
assert(false);
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr int device_count = 1;
|
||||
RAWINPUTDEVICE devices[device_count] = { 0 };
|
||||
// multi-axis mouse (SpaceNavigator, etc.)
|
||||
devices[0].usUsagePage = 0x01;
|
||||
devices[0].usUsage = 0x08;
|
||||
if (! RegisterRawInputDevices(devices, device_count, sizeof(RAWINPUTDEVICE)))
|
||||
BOOST_LOG_TRIVIAL(error) << "RegisterRawInputDevices failed";
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
// propagate event
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
|
||||
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_plater) m_plater->stop_jobs();
|
||||
|
||||
// Weird things happen as the Paint messages are floating around the windows being destructed.
|
||||
// Avoid the Paint messages by hiding the main window.
|
||||
// Also the application closes much faster without these unnecessary screen refreshes.
|
||||
// In addition, there were some crashes due to the Paint events sent to already destructed windows.
|
||||
this->Show(false);
|
||||
|
||||
// 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.
|
||||
wxGetApp().app_config->save();
|
||||
// if (m_plater)
|
||||
// m_plater->print = undef;
|
||||
_3DScene::remove_all_canvases();
|
||||
// Slic3r::GUI::deregister_on_request_update_callback();
|
||||
|
||||
// set to null tabs and a plater
|
||||
// to avoid any manipulations with them from App->wxEVT_IDLE after of the mainframe closing
|
||||
wxGetApp().tabs_list.clear();
|
||||
wxGetApp().plater_ = nullptr;
|
||||
|
||||
this->shutdown();
|
||||
// propagate event
|
||||
event.Skip();
|
||||
});
|
||||
|
|
@ -148,6 +192,65 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
m_plater->show_action_buttons(true);
|
||||
}
|
||||
|
||||
// Called when closing the application and when switching the application language.
|
||||
void MainFrame::shutdown()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_hDeviceNotify) {
|
||||
::UnregisterDeviceNotification(HDEVNOTIFY(m_hDeviceNotify));
|
||||
m_hDeviceNotify = nullptr;
|
||||
}
|
||||
if (m_ulSHChangeNotifyRegister) {
|
||||
SHChangeNotifyDeregister(m_ulSHChangeNotifyRegister);
|
||||
m_ulSHChangeNotifyRegister = 0;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
if (m_plater)
|
||||
m_plater->stop_jobs();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
// Unbinding of wxWidgets event handling in canvases needs to be done here because on MAC,
|
||||
// when closing the application using Command+Q, a mouse event is triggered after this lambda is completed,
|
||||
// causing a crash
|
||||
if (m_plater) m_plater->unbind_canvas_event_handlers();
|
||||
|
||||
// Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours
|
||||
// see: https://github.com/prusa3d/PrusaSlicer/issues/3964
|
||||
if (m_plater) m_plater->reset_canvas_volumes();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
// Weird things happen as the Paint messages are floating around the windows being destructed.
|
||||
// Avoid the Paint messages by hiding the main window.
|
||||
// Also the application closes much faster without these unnecessary screen refreshes.
|
||||
// In addition, there were some crashes due to the Paint events sent to already destructed windows.
|
||||
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);
|
||||
|
||||
// Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater.
|
||||
wxGetApp().removable_drive_manager()->shutdown();
|
||||
|
||||
// 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.
|
||||
wxGetApp().app_config->save();
|
||||
// if (m_plater)
|
||||
// m_plater->print = undef;
|
||||
#if !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
_3DScene::remove_all_canvases();
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
// Slic3r::GUI::deregister_on_request_update_callback();
|
||||
|
||||
// set to null tabs and a plater
|
||||
// to avoid any manipulations with them from App->wxEVT_IDLE after of the mainframe closing
|
||||
wxGetApp().tabs_list.clear();
|
||||
wxGetApp().plater_ = nullptr;
|
||||
}
|
||||
|
||||
void MainFrame::update_title()
|
||||
{
|
||||
wxString title = wxEmptyString;
|
||||
|
|
@ -165,7 +268,8 @@ void MainFrame::update_title()
|
|||
if (idx_plus != build_id.npos) {
|
||||
// Parse what is behind the '+'. If there is a number, then it is a build number after the label, and full build ID is shown.
|
||||
int commit_after_label;
|
||||
if (! boost::starts_with(build_id.data() + idx_plus + 1, "UNKNOWN") && sscanf(build_id.data() + idx_plus + 1, "%d-", &commit_after_label) == 0) {
|
||||
if (! boost::starts_with(build_id.data() + idx_plus + 1, "UNKNOWN") &&
|
||||
(build_id.at(idx_plus + 1) == '-' || sscanf(build_id.data() + idx_plus + 1, "%d-", &commit_after_label) == 0)) {
|
||||
// It is a release build.
|
||||
build_id.erase(build_id.begin() + idx_plus, build_id.end());
|
||||
#if defined(_WIN32) && ! defined(_WIN64)
|
||||
|
|
@ -316,6 +420,27 @@ bool MainFrame::can_send_gcode() const
|
|||
return print_host_opt != nullptr && !print_host_opt->value.empty();
|
||||
}
|
||||
|
||||
bool MainFrame::can_export_gcode_sd() const
|
||||
{
|
||||
if (m_plater == nullptr)
|
||||
return false;
|
||||
|
||||
if (m_plater->model().objects.empty())
|
||||
return false;
|
||||
|
||||
if (m_plater->is_export_gcode_scheduled())
|
||||
return false;
|
||||
|
||||
// TODO:: add other filters
|
||||
|
||||
return wxGetApp().removable_drive_manager()->status().has_removable_drives;
|
||||
}
|
||||
|
||||
bool MainFrame::can_eject() const
|
||||
{
|
||||
return wxGetApp().removable_drive_manager()->status().has_eject;
|
||||
}
|
||||
|
||||
bool MainFrame::can_slice() const
|
||||
{
|
||||
bool bg_proc = wxGetApp().app_config->get("background_processing") == "1";
|
||||
|
|
@ -422,7 +547,7 @@ void MainFrame::init_menubar()
|
|||
m_plater->load_project(filename);
|
||||
else
|
||||
{
|
||||
wxMessageDialog msg(this, _(L("The selected project is no longer available.\nDo you want to remove it from the recent projects list ?")), _(L("Error")), wxYES_NO | wxYES_DEFAULT);
|
||||
wxMessageDialog msg(this, _(L("The selected project is no longer available.\nDo you want to remove it from the recent projects list?")), _(L("Error")), wxYES_NO | wxYES_DEFAULT);
|
||||
if (msg.ShowModal() == wxID_YES)
|
||||
{
|
||||
m_recent_projects.RemoveFileFromHistory(file_id);
|
||||
|
|
@ -486,6 +611,9 @@ void MainFrame::init_menubar()
|
|||
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, "export_gcode", nullptr,
|
||||
[this](){return can_send_gcode(); }, this);
|
||||
m_changeable_menu_items.push_back(item_send_gcode);
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export G-code to SD card / Flash drive")) + dots + "\tCtrl+U", _(L("Export current plate as G-code to SD card / Flash drive")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(true); }, "export_to_sd", nullptr,
|
||||
[this]() {return can_export_gcode_sd(); }, this);
|
||||
export_menu->AppendSeparator();
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater", nullptr,
|
||||
|
|
@ -509,6 +637,10 @@ void MainFrame::init_menubar()
|
|||
[this]() {return true; }, this);
|
||||
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
|
||||
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Ejec&t SD card / Flash drive")) + dots + "\tCtrl+T", _(L("Eject SD card / Flash drive after the G-code was exported to it.")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr,
|
||||
[this]() {return can_eject(); }, this);
|
||||
|
||||
fileMenu->AppendSeparator();
|
||||
|
||||
#if 0
|
||||
|
|
@ -677,9 +809,20 @@ void MainFrame::init_menubar()
|
|||
append_menu_item(viewMenu, wxID_ANY, _(L("Right")) + sep + "&6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); },
|
||||
"", nullptr, [this](){return can_change_view(); }, this);
|
||||
viewMenu->AppendSeparator();
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
wxMenu* options_menu = new wxMenu();
|
||||
append_menu_check_item(options_menu, wxID_ANY, _(L("Show &labels")) + sep + "E", _(L("Show object/instance labels in 3D scene")),
|
||||
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
|
||||
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
|
||||
append_menu_check_item(options_menu, wxID_ANY, _(L("Show &slope")) + sep + "D", _(L("Objects coloring using faces' slope")),
|
||||
[this](wxCommandEvent&) { m_plater->show_view3D_slope(!m_plater->is_view3D_slope_shown()); }, this,
|
||||
[this]() { return m_plater->is_view3D_shown() && !m_plater->is_view3D_layers_editing_enabled(); }, [this]() { return m_plater->is_view3D_slope_shown(); }, this);
|
||||
append_submenu(viewMenu, options_menu, wxID_ANY, _(L("&Options")), "");
|
||||
#else
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _(L("Show &labels")) + sep + "E", _(L("Show object/instance labels in 3D scene")),
|
||||
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
|
||||
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
}
|
||||
|
||||
// Help menu
|
||||
|
|
@ -1029,7 +1172,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
|
|||
wxGetApp().load_current_presets();
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ class MainFrame : public DPIFrame
|
|||
bool can_export_supports() const;
|
||||
bool can_export_gcode() const;
|
||||
bool can_send_gcode() const;
|
||||
bool can_export_gcode_sd() const;
|
||||
bool can_eject() const;
|
||||
bool can_slice() const;
|
||||
bool can_change_view() const;
|
||||
bool can_select() const;
|
||||
|
|
@ -98,6 +100,9 @@ public:
|
|||
MainFrame();
|
||||
~MainFrame() = default;
|
||||
|
||||
// Called when closing the application and when switching the application language.
|
||||
void shutdown();
|
||||
|
||||
Plater* plater() { return m_plater; }
|
||||
|
||||
void update_title();
|
||||
|
|
@ -136,6 +141,12 @@ public:
|
|||
wxNotebook* m_tabpanel { nullptr };
|
||||
wxProgressDialog* m_progress_dialog { nullptr };
|
||||
std::shared_ptr<ProgressStatusBar> m_statusbar;
|
||||
|
||||
#ifdef _WIN32
|
||||
void* m_hDeviceNotify { nullptr };
|
||||
uint32_t m_ulSHChangeNotifyRegister { 0 };
|
||||
static constexpr int WM_USER_MEDIACHANGED { 0x7FFF }; // WM_USER from 0x0400 to 0x7FFF, picking the last one to not interfere with wxWidgets allocation
|
||||
#endif // _WIN32
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -10,67 +10,83 @@
|
|||
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
#include <tbb/mutex.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
#if __APPLE__
|
||||
class Mouse3DHandlerMac;
|
||||
#endif//__APPLE__
|
||||
|
||||
struct Camera;
|
||||
class GLCanvas3D;
|
||||
|
||||
class Mouse3DController
|
||||
{
|
||||
class State
|
||||
{
|
||||
public:
|
||||
static const double DefaultTranslationScale;
|
||||
static const double MaxTranslationDeadzone;
|
||||
static const double DefaultTranslationDeadzone;
|
||||
static const float DefaultRotationScale;
|
||||
static const float MaxRotationDeadzone;
|
||||
static const float DefaultRotationDeadzone;
|
||||
static const double DefaultZoomScale;
|
||||
// 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
|
||||
{
|
||||
static constexpr double DefaultTranslationScale = 2.5;
|
||||
static constexpr double MaxTranslationDeadzone = 0.2;
|
||||
static constexpr double DefaultTranslationDeadzone = 0.5 * MaxTranslationDeadzone;
|
||||
static constexpr float DefaultRotationScale = 1.0f;
|
||||
static constexpr float MaxRotationDeadzone = 0.2f;
|
||||
static constexpr float DefaultRotationDeadzone = 0.5f * MaxRotationDeadzone;
|
||||
static constexpr double DefaultZoomScale = 0.1;
|
||||
|
||||
private:
|
||||
template <typename Number>
|
||||
struct CustomParameters
|
||||
{
|
||||
Number scale;
|
||||
Number deadzone;
|
||||
|
||||
CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct InputQueue
|
||||
{
|
||||
size_t max_size;
|
||||
std::queue<T> queue;
|
||||
CustomParameters<double> translation { DefaultTranslationScale, DefaultTranslationDeadzone };
|
||||
CustomParameters<float> rotation { DefaultRotationScale, DefaultRotationDeadzone };
|
||||
CustomParameters<double> zoom { DefaultZoomScale, 0.0 };
|
||||
// Do not process button presses from 3DConnexion device, let the user map the 3DConnexion keys in 3DConnexion driver.
|
||||
bool buttons_enabled { false };
|
||||
// The default value of 15 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
|
||||
// and playing with the imgui dialog which shows by pressing CTRL+M
|
||||
size_t input_queue_max_size { 15 };
|
||||
// Whether to swap Y/Z axes or not.
|
||||
bool swap_yz{ false };
|
||||
};
|
||||
|
||||
// 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
|
||||
// and playing with the imgui dialog which shows by pressing CTRL+M
|
||||
InputQueue() : max_size(5) {}
|
||||
// Queue of the 3DConnexion input events (translations, rotations, button presses).
|
||||
class State
|
||||
{
|
||||
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 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(); }
|
||||
|
||||
Vec3d vector;
|
||||
unsigned int type_or_buttons;
|
||||
|
||||
static constexpr unsigned int TranslationType = std::numeric_limits<unsigned int>::max();
|
||||
static constexpr unsigned int RotationType = TranslationType - 1;
|
||||
};
|
||||
|
||||
InputQueue<Vec3d> m_translation;
|
||||
InputQueue<Vec3f> m_rotation;
|
||||
std::queue<unsigned int> m_buttons;
|
||||
|
||||
bool m_buttons_enabled;
|
||||
|
||||
CustomParameters<double> m_translation_params;
|
||||
CustomParameters<float> m_rotation_params;
|
||||
CustomParameters<double> m_zoom_params;
|
||||
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.
|
||||
// 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
|
||||
|
|
@ -78,131 +94,143 @@ class Mouse3DController
|
|||
// 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_idle() through the call to the apply() method
|
||||
std::atomic<unsigned int> m_mouse_wheel_counter;
|
||||
|
||||
#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
|
||||
unsigned int m_mouse_wheel_counter { 0 };
|
||||
#endif /* WIN32 */
|
||||
|
||||
public:
|
||||
State();
|
||||
|
||||
void append_translation(const Vec3d& translation);
|
||||
void append_rotation(const Vec3f& rotation);
|
||||
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(); }
|
||||
// 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_rotation(const Vec3f& rotation, size_t input_queue_max_size);
|
||||
void append_button(unsigned int id, size_t input_queue_max_size);
|
||||
|
||||
#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();
|
||||
|
||||
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; }
|
||||
#endif /* WIN32 */
|
||||
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); }
|
||||
Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); }
|
||||
unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; }
|
||||
|
||||
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(); }
|
||||
unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.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; }
|
||||
size_t get_buttons_queue_max_size() const { return m_buttons_queue_max_size; }
|
||||
Vec3d get_first_vector_of_type(unsigned int type) const {
|
||||
tbb::mutex::scoped_lock lock(m_input_queue_mutex);
|
||||
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;
|
||||
}
|
||||
size_t input_queue_size_current() const {
|
||||
tbb::mutex::scoped_lock lock(m_input_queue_mutex);
|
||||
return m_input_queue.size();
|
||||
}
|
||||
std::atomic<size_t> input_queue_max_size_achieved;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
|
||||
size_t get_queues_max_size() const { return m_translation.max_size; }
|
||||
void set_queues_max_size(size_t size);
|
||||
|
||||
// return true if any change to the camera took place
|
||||
bool apply(Camera& camera);
|
||||
// Apply the 3DConnexion events stored in the input queue, reset the input queue.
|
||||
// Returns true if any change to the camera took place.
|
||||
bool apply(const Params ¶ms, Camera& camera);
|
||||
};
|
||||
|
||||
bool m_initialized;
|
||||
mutable State m_state;
|
||||
std::thread m_thread;
|
||||
hid_device* m_device;
|
||||
std::string m_device_str;
|
||||
bool m_running;
|
||||
bool m_mac_mouse_connected;
|
||||
mutable bool m_show_settings_dialog;
|
||||
// set to true when ther user closes the dialog by clicking on [X] or [Close] buttons
|
||||
mutable bool m_settings_dialog_closed_by_user;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_last_time;
|
||||
// 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;
|
||||
std::atomic<bool> m_connected { false };
|
||||
std::string m_device_str;
|
||||
|
||||
#if ! __APPLE__
|
||||
// Worker thread for enumerating devices, connecting, reading data from the device and closing the device.
|
||||
std::thread m_thread;
|
||||
hid_device* m_device { nullptr };
|
||||
// Using m_stop_condition_mutex to synchronize m_stop.
|
||||
bool m_stop { false };
|
||||
#ifdef _WIN32
|
||||
std::atomic<bool> m_wakeup { false };
|
||||
#endif /* _WIN32 */
|
||||
// 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:
|
||||
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();
|
||||
// Stop the background thread (Windows and Linux).
|
||||
// Disconnect from a 3DConnextion driver (OSX).
|
||||
// Call save_config() after shutdown().
|
||||
void shutdown();
|
||||
|
||||
bool is_device_connected() const { return m_device != nullptr || m_mac_mouse_connected; }
|
||||
bool is_running() const { return m_running || m_mac_mouse_connected; }
|
||||
bool connected() const { return m_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
|
||||
bool handle_raw_input_win32(const unsigned char *data, const int packet_lenght);
|
||||
|
||||
// Called by Win32 HID enumeration callback.
|
||||
void device_attached(const std::string &device);
|
||||
|
||||
// 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(); }
|
||||
#endif // WIN32
|
||||
|
||||
// Apply the received 3DConnexion mouse events to the camera. Called from the UI rendering thread.
|
||||
bool apply(Camera& camera);
|
||||
|
||||
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;
|
||||
|
||||
typedef std::array<double, 6> DataPacketAxis;
|
||||
void handle_input_axis(const DataPacketAxis& packet);
|
||||
#if ! __APPLE__
|
||||
private:
|
||||
bool connect_device();
|
||||
void disconnect_device();
|
||||
void start();
|
||||
void stop() { m_running = false; }
|
||||
|
||||
typedef std::array<unsigned char, 13> DataPacketRaw;
|
||||
// secondary thread methods
|
||||
void run();
|
||||
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__
|
||||
Mouse3DHandlerMac* m_handler_mac;
|
||||
#endif//__APPLE__
|
||||
typedef std::array<unsigned char, 13> DataPacketRaw;
|
||||
|
||||
// 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_length, const Params ¶ms, State &state_in_out);
|
||||
// The following is called by handle_input() from the worker thread.
|
||||
static bool handle_packet(const DataPacketRaw& packet, const int packet_length, const Params ¶ms, State &state_in_out);
|
||||
static bool handle_packet_translation(const DataPacketRaw& packet, const Params ¶ms, State &state_in_out);
|
||||
static bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte, const Params ¶ms, State &state_in_out);
|
||||
static bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size, const Params ¶ms, State &state_in_out);
|
||||
#endif /* __APPLE__ */
|
||||
};
|
||||
|
||||
#if __APPLE__
|
||||
class Mouse3DHandlerMac{
|
||||
public:
|
||||
Mouse3DHandlerMac(Mouse3DController* controller);
|
||||
~Mouse3DHandlerMac();
|
||||
|
||||
bool available();
|
||||
};
|
||||
#endif//__APPLE__
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
#endif // slic3r_Mouse3DController_hpp_
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include "Mouse3DController.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
|
|
@ -10,7 +9,7 @@
|
|||
#include <cstdio>
|
||||
|
||||
|
||||
static Slic3r::GUI::Mouse3DController* mouse_3d_controller = NULL;
|
||||
static Slic3r::GUI::Mouse3DController* mouse_3d_controller = nullptr;
|
||||
|
||||
static uint16_t clientID = 0;
|
||||
|
||||
|
|
@ -65,7 +64,7 @@ typedef int16_t (*ConnexionClientControl_ptr)(uint16_t clientID,
|
|||
int32_t param,
|
||||
int32_t *result);
|
||||
|
||||
#define DECLARE_FUNC(name) name##_ptr name = NULL
|
||||
#define DECLARE_FUNC(name) name##_ptr name = nullptr
|
||||
|
||||
DECLARE_FUNC(SetConnexionHandlers);
|
||||
DECLARE_FUNC(InstallConnexionHandlers);
|
||||
|
|
@ -79,15 +78,12 @@ static void *load_func(void *module, const char *func_name)
|
|||
{
|
||||
void *func = dlsym(module, func_name);
|
||||
|
||||
//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
if (func) {
|
||||
BOOST_LOG_TRIVIAL(info) << func_name <<" loaded";
|
||||
}
|
||||
else {
|
||||
//printf("<!> %s\n", dlerror());
|
||||
BOOST_LOG_TRIVIAL(error) <<"loading 3dx drivers dlsym error: "<< dlerror();
|
||||
}
|
||||
//#endif
|
||||
|
||||
return func;
|
||||
}
|
||||
|
|
@ -98,9 +94,8 @@ static void *module; // handle to the whole driver
|
|||
|
||||
static bool load_driver_functions()
|
||||
{
|
||||
if (driver_loaded) {
|
||||
if (driver_loaded)
|
||||
return true;
|
||||
}
|
||||
|
||||
module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient",
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
|
|
@ -109,15 +104,14 @@ static bool load_driver_functions()
|
|||
BOOST_LOG_TRIVIAL(info) << "loading 3dx drivers";
|
||||
LOAD_FUNC(SetConnexionHandlers);
|
||||
|
||||
if (SetConnexionHandlers != NULL) {
|
||||
if (SetConnexionHandlers != nullptr) {
|
||||
driver_loaded = true;
|
||||
has_new_driver = true;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers";
|
||||
BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers";
|
||||
LOAD_FUNC(InstallConnexionHandlers);
|
||||
|
||||
driver_loaded = (InstallConnexionHandlers != NULL);
|
||||
driver_loaded = (InstallConnexionHandlers != nullptr);
|
||||
}
|
||||
|
||||
if (driver_loaded) {
|
||||
|
|
@ -128,17 +122,11 @@ static bool load_driver_functions()
|
|||
LOAD_FUNC(ConnexionClientControl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else {
|
||||
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");
|
||||
printf("new: %s\n", has_new_driver ? "YES" : "NO");
|
||||
#endif
|
||||
BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: "<< driver_loaded ? "YES" : "NO" ;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: " << (driver_loaded ? (has_new_driver ? "YES, new" : "YES, old") : "NO");
|
||||
return driver_loaded;
|
||||
}
|
||||
|
||||
|
|
@ -149,29 +137,25 @@ static void unload_driver()
|
|||
|
||||
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";
|
||||
// determine exactly which device is plugged in
|
||||
int32_t result;
|
||||
ConnexionClientControl(clientID, kConnexionCtlGetDeviceID, 0, &result);
|
||||
int16_t vendorID = result >> 16;
|
||||
int16_t productID = result & 0xffff;
|
||||
|
||||
int vendorID = result >> 16;
|
||||
int productID = result & 0xffff;
|
||||
//TODO: verify device
|
||||
|
||||
|
||||
mouse_3d_controller->set_mac_mouse_connected(true);
|
||||
char buf[64];
|
||||
sprintf(buf, "VID%04X,PID%04X", vendorID, productID);
|
||||
mouse_3d_controller->connected(buf);
|
||||
}
|
||||
|
||||
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";
|
||||
mouse_3d_controller->set_mac_mouse_connected(true);
|
||||
// not accessible in a free function
|
||||
// assert(m_connected);
|
||||
// assert(! m_device_str.empty());
|
||||
mouse_3d_controller->disconnected();
|
||||
}
|
||||
|
||||
static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
|
||||
|
|
@ -181,21 +165,16 @@ static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
|
|||
if (s->client == clientID) {
|
||||
switch (s->command) {
|
||||
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.
|
||||
*/
|
||||
//Actually we are getting values way over 1024. Max is probably 2048 now.
|
||||
std::array<double, 6> packet;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
// 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.
|
||||
Slic3r::GUI::Mouse3DController::DataPacketAxis packet;
|
||||
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.
|
||||
}
|
||||
mouse_3d_controller->handle_input_axis(packet);
|
||||
|
||||
|
||||
mouse_3d_controller->handle_input(packet);
|
||||
break;
|
||||
}
|
||||
case kConnexionCmdHandleButtons:
|
||||
break;
|
||||
break;
|
||||
case kConnexionCmdAppSpecific:
|
||||
break;
|
||||
default:
|
||||
|
|
@ -203,53 +182,44 @@ static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Slic3r {
|
||||
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()) {
|
||||
mouse_3d_controller = controller;
|
||||
mouse_3d_controller = this;
|
||||
|
||||
uint16_t error;
|
||||
if (has_new_driver) {
|
||||
error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false);
|
||||
}
|
||||
else {
|
||||
error = InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved);
|
||||
}
|
||||
uint16_t error = has_new_driver ?
|
||||
SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false) :
|
||||
InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved);
|
||||
|
||||
if (error) {
|
||||
return;
|
||||
if (! error) {
|
||||
// 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).
|
||||
//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")
|
||||
clientID = RegisterConnexionClient(
|
||||
0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis);
|
||||
BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered";
|
||||
}
|
||||
|
||||
// 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).
|
||||
//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")
|
||||
|
||||
clientID = RegisterConnexionClient(
|
||||
0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis);
|
||||
BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered";
|
||||
}
|
||||
}
|
||||
|
||||
Mouse3DHandlerMac::~Mouse3DHandlerMac()
|
||||
void Mouse3DController::shutdown()
|
||||
{
|
||||
if (driver_loaded) {
|
||||
UnregisterConnexionClient(clientID);
|
||||
CleanupConnexionHandlers();
|
||||
unload_driver();
|
||||
}
|
||||
// Copy the current parameters to parameter database, mark the device as disconnected.
|
||||
this->disconnected();
|
||||
mouse_3d_controller = nullptr;
|
||||
}
|
||||
|
||||
bool Mouse3DHandlerMac::available()
|
||||
{
|
||||
return driver_loaded;
|
||||
}
|
||||
|
||||
}}//namespace Slic3r::GUI
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
|
|||
html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1));
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
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 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;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,11 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel
|
|||
}
|
||||
|
||||
void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = nullptr*/) {
|
||||
if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width) {
|
||||
if ( line.full_width && (
|
||||
line.sizer != nullptr ||
|
||||
line.widget != nullptr ||
|
||||
!line.get_extra_widgets().empty() )
|
||||
) {
|
||||
if (line.sizer != nullptr) {
|
||||
sizer->Add(line.sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
|
||||
return;
|
||||
|
|
@ -122,6 +126,17 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
sizer->Add(line.widget(this->ctrl_parent()), 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
|
||||
return;
|
||||
}
|
||||
if (!line.get_extra_widgets().empty()) {
|
||||
const auto h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(h_sizer, 1, wxEXPAND | wxALL, wxOSX ? 0 : 15);
|
||||
|
||||
bool is_first_item = true;
|
||||
for (auto extra_widget : line.get_extra_widgets()) {
|
||||
h_sizer->Add(extra_widget(this->ctrl_parent()), is_first_item ? 1 : 0, wxLEFT, 15);
|
||||
is_first_item = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto option_set = line.get_options();
|
||||
|
|
@ -412,7 +427,9 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
|
|||
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
|
||||
value = int(nozzle_diameter->values.size());
|
||||
}
|
||||
else if (m_opt_map.find(opt_key) == m_opt_map.end() || opt_key == "bed_shape") {
|
||||
else if (m_opt_map.find(opt_key) == m_opt_map.end() ||
|
||||
// This option don't have corresponded field
|
||||
opt_key == "bed_shape" || opt_key == "compatible_printers" || opt_key == "compatible_prints" ) {
|
||||
value = get_config_value(config, opt_key);
|
||||
change_opt_value(*m_config, opt_key, value);
|
||||
return;
|
||||
|
|
@ -629,7 +646,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
|||
ret = static_cast<wxString>(config.opt_string(opt_key));
|
||||
break;
|
||||
case coStrings:
|
||||
if (opt_key.compare("compatible_printers") == 0) {
|
||||
if (opt_key == "compatible_printers" || opt_key == "compatible_prints") {
|
||||
ret = config.option<ConfigOptionStrings>(opt_key)->values;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -42,6 +42,10 @@ class ObjectList;
|
|||
class GLCanvas3D;
|
||||
class Mouse3DController;
|
||||
struct Camera;
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
class Bed3D;
|
||||
class GLToolbar;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
|
||||
|
||||
|
|
@ -69,7 +73,7 @@ public:
|
|||
void set_extruder_idx(const int extr_idx) { extruder_idx = extr_idx; }
|
||||
int get_extruder_idx() const { return extruder_idx; }
|
||||
int em_unit() const { return m_em_unit; }
|
||||
void check_selection();
|
||||
void check_selection(int selection);
|
||||
|
||||
void msw_rescale();
|
||||
|
||||
|
|
@ -171,6 +175,13 @@ public:
|
|||
bool are_view3D_labels_shown() const;
|
||||
void show_view3D_labels(bool show);
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
bool is_view3D_slope_shown() const;
|
||||
void show_view3D_slope(bool show);
|
||||
|
||||
bool is_view3D_layers_editing_enabled() const;
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
// Called after the Preferences dialog is closed and the program settings are saved.
|
||||
// Update the UI based on the current preferences.
|
||||
void update_ui_from_settings();
|
||||
|
|
@ -205,12 +216,11 @@ public:
|
|||
void changed_object(int obj_idx);
|
||||
void changed_objects(const std::vector<size_t>& object_idxs);
|
||||
void schedule_background_process(bool schedule = true);
|
||||
bool is_background_process_running() const;
|
||||
bool is_background_process_update_scheduled() const;
|
||||
void suppress_background_process(const bool stop_background_process) ;
|
||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||
void send_gcode();
|
||||
void eject_drive();
|
||||
void drive_ejected_callback();
|
||||
|
||||
void take_snapshot(const std::string &snapshot_name);
|
||||
void take_snapshot(const wxString &snapshot_name);
|
||||
|
|
@ -250,6 +260,10 @@ public:
|
|||
BoundingBoxf bed_shape_bb() const;
|
||||
|
||||
void set_current_canvas_as_dirty();
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
void unbind_canvas_event_handlers();
|
||||
void reset_canvas_volumes();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
PrinterTechnology printer_technology() const;
|
||||
void set_printer_technology(PrinterTechnology printer_technology);
|
||||
|
|
@ -278,12 +292,22 @@ public:
|
|||
bool init_view_toolbar();
|
||||
|
||||
const Camera& get_camera() const;
|
||||
Camera& get_camera();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const Bed3D& get_bed() const;
|
||||
Bed3D& get_bed();
|
||||
|
||||
const GLToolbar& get_view_toolbar() const;
|
||||
GLToolbar& get_view_toolbar();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
const Mouse3DController& get_mouse3d_controller() const;
|
||||
Mouse3DController& get_mouse3d_controller();
|
||||
|
||||
void set_bed_shape() const;
|
||||
|
||||
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
|
||||
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
|
||||
class SuppressSnapshots
|
||||
{
|
||||
public:
|
||||
|
|
@ -316,10 +340,22 @@ public:
|
|||
Plater *m_plater;
|
||||
};
|
||||
|
||||
bool inside_snapshot_capture();
|
||||
|
||||
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
|
||||
bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition);
|
||||
bool PopupMenu(wxMenu *menu, int x, int y) { return this->PopupMenu(menu, wxPoint(x, y)); }
|
||||
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
// Set true during PopupMenu() tracking to suppress immediate error message boxes.
|
||||
// The error messages are collected to m_tracking_popup_menu_error_message instead and these error messages
|
||||
// are shown after the pop-up dialog closes.
|
||||
bool m_tracking_popup_menu = false;
|
||||
wxString m_tracking_popup_menu_error_message;
|
||||
|
||||
void suppress_snapshots();
|
||||
void allow_snapshots();
|
||||
|
||||
|
|
@ -332,7 +368,7 @@ public:
|
|||
SuppressBackgroundProcessingUpdate();
|
||||
~SuppressBackgroundProcessingUpdate();
|
||||
private:
|
||||
bool m_was_running;
|
||||
bool m_was_scheduled;
|
||||
};
|
||||
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -189,6 +189,9 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
|
|||
default_materials_field = section.second.get<std::string>("default_filaments", "");
|
||||
if (Slic3r::unescape_strings_cstyle(default_materials_field, model.default_materials)) {
|
||||
Slic3r::sort_remove_duplicates(model.default_materials);
|
||||
if (! model.default_materials.empty() && model.default_materials.front().empty())
|
||||
// An empty material was inserted into the list of default materials. Remove it.
|
||||
model.default_materials.erase(model.default_materials.begin());
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed default_materials field: `%2%`") % id % default_materials_field;
|
||||
}
|
||||
|
|
@ -378,7 +381,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
|
|||
return;
|
||||
is_visible = app_config.get_variant(vendor->id, model, variant);
|
||||
} else if (type == TYPE_FILAMENT || type == TYPE_SLA_MATERIAL) {
|
||||
const char *section_name = (type == TYPE_FILAMENT) ? "filaments" : "sla_materials";
|
||||
const std::string §ion_name = (type == TYPE_FILAMENT) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS;
|
||||
if (app_config.has_section(section_name)) {
|
||||
// Check whether this profile is marked as "installed" in PrusaSlicer.ini,
|
||||
// or whether a profile is marked as "installed", which this profile may have been renamed from.
|
||||
|
|
@ -410,7 +413,7 @@ const std::vector<std::string>& Preset::print_options()
|
|||
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
|
||||
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
|
||||
"bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
|
||||
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
|
||||
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
|
||||
"min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
|
||||
"raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",
|
||||
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",
|
||||
|
|
@ -851,7 +854,7 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
|
|||
return preset;
|
||||
}
|
||||
|
||||
void PresetCollection::save_current_preset(const std::string &new_name)
|
||||
void PresetCollection::save_current_preset(const std::string &new_name, bool detach)
|
||||
{
|
||||
// 1) Find the preset with a new_name or create a new one,
|
||||
// initialize it with the edited config.
|
||||
|
|
@ -866,6 +869,13 @@ void PresetCollection::save_current_preset(const std::string &new_name)
|
|||
preset.config = std::move(m_edited_preset.config);
|
||||
// The newly saved preset will be activated -> make it visible.
|
||||
preset.is_visible = true;
|
||||
if (detach) {
|
||||
// Clear the link to the parent profile.
|
||||
preset.vendor = nullptr;
|
||||
preset.inherits().clear();
|
||||
preset.alias.clear();
|
||||
preset.renamed_from.clear();
|
||||
}
|
||||
} else {
|
||||
// Creating a new preset.
|
||||
Preset &preset = *m_presets.insert(it, m_edited_preset);
|
||||
|
|
@ -874,7 +884,12 @@ void PresetCollection::save_current_preset(const std::string &new_name)
|
|||
preset.name = new_name;
|
||||
preset.file = this->path_from_name(new_name);
|
||||
preset.vendor = nullptr;
|
||||
if (preset.is_system) {
|
||||
preset.alias.clear();
|
||||
preset.renamed_from.clear();
|
||||
if (detach) {
|
||||
// Clear the link to the parent profile.
|
||||
inherits.clear();
|
||||
} else if (preset.is_system) {
|
||||
// Inheriting from a system preset.
|
||||
inherits = /* preset.vendor->name + "/" + */ old_name;
|
||||
} else if (inherits.empty()) {
|
||||
|
|
@ -1017,6 +1032,14 @@ const std::string& PresetCollection::get_preset_name_by_alias(const std::string&
|
|||
return alias;
|
||||
}
|
||||
|
||||
const std::string* PresetCollection::get_preset_name_renamed(const std::string &old_name) const
|
||||
{
|
||||
auto it_renamed = m_map_system_profile_renamed.find(old_name);
|
||||
if (it_renamed != m_map_system_profile_renamed.end())
|
||||
return &it_renamed->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string& PresetCollection::get_suffix_modified() {
|
||||
return g_suffix_modified;
|
||||
}
|
||||
|
|
@ -1061,6 +1084,7 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
|
|||
const ConfigOption *opt = active_printer.preset.config.option("nozzle_diameter");
|
||||
if (opt)
|
||||
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
|
||||
bool some_compatible = false;
|
||||
for (size_t idx_preset = m_num_default_presets; idx_preset < m_presets.size(); ++ idx_preset) {
|
||||
bool selected = idx_preset == m_idx_selected;
|
||||
Preset &preset_selected = m_presets[idx_preset];
|
||||
|
|
@ -1069,6 +1093,7 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
|
|||
const PresetWithVendorProfile this_preset_with_vendor_profile = this->get_preset_with_vendor_profile(preset_edited);
|
||||
bool was_compatible = preset_edited.is_compatible;
|
||||
preset_edited.is_compatible = is_compatible_with_printer(this_preset_with_vendor_profile, active_printer, &config);
|
||||
some_compatible |= preset_edited.is_compatible;
|
||||
if (active_print != nullptr)
|
||||
preset_edited.is_compatible &= is_compatible_with_print(this_preset_with_vendor_profile, *active_print, active_printer);
|
||||
if (! preset_edited.is_compatible && selected &&
|
||||
|
|
@ -1077,6 +1102,10 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
|
|||
if (selected)
|
||||
preset_selected.is_compatible = preset_edited.is_compatible;
|
||||
}
|
||||
// Update visibility of the default profiles here if the defaults are suppressed, the current profile is not compatible and we don't want to select another compatible profile.
|
||||
if (m_idx_selected >= m_num_default_presets && m_default_suppressed)
|
||||
for (size_t i = 0; i < m_num_default_presets; ++ i)
|
||||
m_presets[i].is_visible = ! some_compatible;
|
||||
return m_idx_selected;
|
||||
}
|
||||
|
||||
|
|
@ -1225,7 +1254,7 @@ void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui)
|
|||
|
||||
ui->SetSelection(selected_preset_item);
|
||||
ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip);
|
||||
ui->check_selection();
|
||||
ui->check_selection(selected_preset_item);
|
||||
ui->Thaw();
|
||||
|
||||
// Update control min size after rescale (changed Display DPI under MSW)
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ public:
|
|||
|
||||
static void update_suffix_modified();
|
||||
static const std::string& suffix_modified();
|
||||
static std::string remove_suffix_modified(const std::string& name);
|
||||
static void normalize(DynamicPrintConfig &config);
|
||||
// Report configuration fields, which are misplaced into a wrong group, remove them from the config.
|
||||
static std::string remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config);
|
||||
|
|
@ -244,7 +245,6 @@ public:
|
|||
protected:
|
||||
friend class PresetCollection;
|
||||
friend class PresetBundle;
|
||||
static std::string remove_suffix_modified(const std::string &name);
|
||||
};
|
||||
|
||||
bool is_compatible_with_print (const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print, const PresetWithVendorProfile &active_printer);
|
||||
|
|
@ -312,7 +312,7 @@ public:
|
|||
// Save the preset under a new name. If the name is different from the old one,
|
||||
// a new preset is stored into the list of presets.
|
||||
// All presets are marked as not modified and the new preset is activated.
|
||||
void save_current_preset(const std::string &new_name);
|
||||
void save_current_preset(const std::string &new_name, bool detach = false);
|
||||
|
||||
// Delete the current preset, activate the first visible preset.
|
||||
// returns true if the preset was deleted successfully.
|
||||
|
|
@ -361,7 +361,8 @@ public:
|
|||
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
|
||||
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
|
||||
|
||||
const std::string& get_preset_name_by_alias(const std::string& alias) const;
|
||||
const std::string& get_preset_name_by_alias(const std::string& alias) const;
|
||||
const std::string* get_preset_name_renamed(const std::string &old_name) const;
|
||||
|
||||
// used to update preset_choice from Tab
|
||||
const std::deque<Preset>& get_presets() const { return m_presets; }
|
||||
|
|
|
|||
|
|
@ -291,17 +291,7 @@ std::string PresetBundle::load_system_presets()
|
|||
this->reset(false);
|
||||
}
|
||||
|
||||
this->prints .update_map_system_profile_renamed();
|
||||
this->sla_prints .update_map_system_profile_renamed();
|
||||
this->filaments .update_map_system_profile_renamed();
|
||||
this->sla_materials.update_map_system_profile_renamed();
|
||||
this->printers .update_map_system_profile_renamed();
|
||||
|
||||
this->prints .update_map_alias_to_profile_name();
|
||||
this->sla_prints .update_map_alias_to_profile_name();
|
||||
this->filaments .update_map_alias_to_profile_name();
|
||||
this->sla_materials.update_map_alias_to_profile_name();
|
||||
|
||||
this->update_system_maps();
|
||||
return errors_cummulative;
|
||||
}
|
||||
|
||||
|
|
@ -326,6 +316,20 @@ std::vector<std::string> PresetBundle::merge_presets(PresetBundle &&other)
|
|||
return duplicate_prints;
|
||||
}
|
||||
|
||||
void PresetBundle::update_system_maps()
|
||||
{
|
||||
this->prints .update_map_system_profile_renamed();
|
||||
this->sla_prints .update_map_system_profile_renamed();
|
||||
this->filaments .update_map_system_profile_renamed();
|
||||
this->sla_materials.update_map_system_profile_renamed();
|
||||
this->printers .update_map_system_profile_renamed();
|
||||
|
||||
this->prints .update_map_alias_to_profile_name();
|
||||
this->sla_prints .update_map_alias_to_profile_name();
|
||||
this->filaments .update_map_alias_to_profile_name();
|
||||
this->sla_materials.update_map_alias_to_profile_name();
|
||||
}
|
||||
|
||||
static inline std::string remove_ini_suffix(const std::string &name)
|
||||
{
|
||||
std::string out = name;
|
||||
|
|
@ -339,9 +343,9 @@ static inline std::string remove_ini_suffix(const std::string &name)
|
|||
// If the "vendor" section is missing, enable all models and variants of the particular vendor.
|
||||
void PresetBundle::load_installed_printers(const AppConfig &config)
|
||||
{
|
||||
for (auto &preset : printers) {
|
||||
this->update_system_maps();
|
||||
for (auto &preset : printers)
|
||||
preset.set_visible_from_appconfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& preset_type, const std::string& alias) const
|
||||
|
|
@ -369,7 +373,7 @@ void PresetBundle::load_installed_filaments(AppConfig &config)
|
|||
if (printer.is_visible && printer.printer_technology() == ptFFF) {
|
||||
const PresetWithVendorProfile printer_with_vendor_profile = printers.get_preset_with_vendor_profile(printer);
|
||||
for (const Preset &filament : filaments)
|
||||
if (is_compatible_with_printer(filaments.get_preset_with_vendor_profile(filament), printer_with_vendor_profile))
|
||||
if (filament.is_system && is_compatible_with_printer(filaments.get_preset_with_vendor_profile(filament), printer_with_vendor_profile))
|
||||
compatible_filaments.insert(&filament);
|
||||
}
|
||||
// and mark these filaments as installed, therefore this code will not be executed at the next start of the application.
|
||||
|
|
@ -392,7 +396,7 @@ void PresetBundle::load_installed_sla_materials(AppConfig &config)
|
|||
if (printer.is_visible && printer.printer_technology() == ptSLA) {
|
||||
const PresetWithVendorProfile printer_with_vendor_profile = printers.get_preset_with_vendor_profile(printer);
|
||||
for (const Preset &material : sla_materials)
|
||||
if (is_compatible_with_printer(sla_materials.get_preset_with_vendor_profile(material), printer_with_vendor_profile))
|
||||
if (material.is_system && is_compatible_with_printer(sla_materials.get_preset_with_vendor_profile(material), printer_with_vendor_profile))
|
||||
comp_sla_materials.insert(&material);
|
||||
}
|
||||
// and mark these SLA materials as installed, therefore this code will not be executed at the next start of the application.
|
||||
|
|
@ -705,7 +709,10 @@ void PresetBundle::load_config_file(const std::string &path)
|
|||
boost::nowide::ifstream ifs(path);
|
||||
boost::property_tree::read_ini(ifs, tree);
|
||||
} catch (const std::ifstream::failure &err) {
|
||||
throw std::runtime_error(std::string("The config file cannot be loaded: ") + path + "\n\tReason: " + err.what());
|
||||
throw std::runtime_error(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what());
|
||||
} catch (const boost::property_tree::file_parser_error &err) {
|
||||
throw std::runtime_error((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%")
|
||||
% err.filename() % err.message() % err.line()).str());
|
||||
} catch (const std::runtime_error &err) {
|
||||
throw std::runtime_error(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
|
||||
}
|
||||
|
|
@ -1066,7 +1073,11 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, co
|
|||
// Iterate in a reverse order, so the last change will be placed first in merged.
|
||||
for (auto it_inherits = prst->inherits.rbegin(); it_inherits != prst->inherits.rend(); ++ it_inherits)
|
||||
for (auto it = (*it_inherits)->node->begin(); it != (*it_inherits)->node->end(); ++ it)
|
||||
if (prst->node->find(it->first) == prst->node->not_found())
|
||||
if (it->first == "renamed_from") {
|
||||
// Don't inherit "renamed_from" flag, it does not make sense. The "renamed_from" flag only makes sense for a concrete preset.
|
||||
if (boost::starts_with((*it_inherits)->name, "*"))
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Nonpublic intermediate preset %1% contains a \"renamed_from\" field, which is ignored") % (*it_inherits)->name;
|
||||
} else if (prst->node->find(it->first) == prst->node->not_found())
|
||||
prst->node->add_child(it->first, it->second);
|
||||
}
|
||||
|
||||
|
|
@ -1109,8 +1120,13 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||
const VendorProfile *vendor_profile = nullptr;
|
||||
if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) {
|
||||
auto vp = VendorProfile::from_ini(tree, path);
|
||||
if (vp.num_variants() == 0)
|
||||
if (vp.models.size() == 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No printer model defined.") % path;
|
||||
return 0;
|
||||
} else if (vp.num_variants() == 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No printer variant defined") % path;
|
||||
return 0;
|
||||
}
|
||||
vendor_profile = &this->vendors.insert({vp.id, vp}).first->second;
|
||||
}
|
||||
|
||||
|
|
@ -1599,21 +1615,23 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre
|
|||
|
||||
// To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so
|
||||
// set a bitmap height to m_bitmapLock->GetHeight()
|
||||
// Note, under OSX we should use a ScaledHeight because of Retina scale
|
||||
//
|
||||
// To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size.
|
||||
// But for some display scaling (for example 125% or 175%) normal_icon_width differs from icon width.
|
||||
// So:
|
||||
// for nonsystem presets set a width of empty bitmap to m_bitmapLock->GetWidth()
|
||||
// for compatible presets set a width of empty bitmap to m_bitmapIncompatible->GetWidth()
|
||||
//
|
||||
// Note, under OSX we should use a Scaled Height/Width because of Retina scale
|
||||
#ifdef __APPLE__
|
||||
const int icon_height = m_bitmapLock->GetScaledHeight();
|
||||
const int lock_icon_width = m_bitmapLock->GetScaledWidth();
|
||||
const int flag_icon_width = m_bitmapIncompatible->GetScaledWidth();
|
||||
#else
|
||||
const int icon_height = m_bitmapLock->GetHeight();
|
||||
#endif
|
||||
|
||||
/* To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size.
|
||||
* But for some display scaling (for example 125% or 175%) normal_icon_width differs from icon width.
|
||||
* So:
|
||||
* for nonsystem presets set a width of empty bitmap to m_bitmapLock->GetWidth()
|
||||
* for compatible presets set a width of empty bitmap to m_bitmapIncompatible->GetWidth()
|
||||
**/
|
||||
const int lock_icon_width = m_bitmapLock->GetWidth();
|
||||
const int flag_icon_width = m_bitmapIncompatible->GetWidth();
|
||||
#endif
|
||||
|
||||
wxString tooltip = "";
|
||||
|
||||
|
|
@ -1719,7 +1737,7 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre
|
|||
|
||||
ui->SetSelection(selected_preset_item);
|
||||
ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip);
|
||||
ui->check_selection();
|
||||
ui->check_selection(selected_preset_item);
|
||||
ui->Thaw();
|
||||
|
||||
// Update control min size after rescale (changed Display DPI under MSW)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ public:
|
|||
PresetCollection sla_prints;
|
||||
PresetCollection filaments;
|
||||
PresetCollection sla_materials;
|
||||
PresetCollection& materials(PrinterTechnology pt) { return pt == ptFFF ? this->filaments : this->sla_materials; }
|
||||
const PresetCollection& materials(PrinterTechnology pt) const { return pt == ptFFF ? this->filaments : this->sla_materials; }
|
||||
PrinterPresetCollection printers;
|
||||
// Filament preset names for a multi-extruder or multi-material print.
|
||||
// extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
|
||||
|
|
@ -144,6 +146,8 @@ private:
|
|||
std::string load_system_presets();
|
||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||
std::vector<std::string> merge_presets(PresetBundle &&other);
|
||||
// Update renamed_from and alias maps of system profiles.
|
||||
void update_system_maps();
|
||||
|
||||
// Set the is_visible flag for filaments and sla materials,
|
||||
// apply defaults based on enabled printers when no filaments/materials are installed.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "RemovableDriveManager.hpp"
|
||||
#include <iostream>
|
||||
#include "boost/nowide/convert.hpp"
|
||||
#include <libslic3r/libslic3r.h>
|
||||
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#if _WIN32
|
||||
#include <windows.h>
|
||||
|
|
@ -9,11 +11,9 @@
|
|||
#include <shlwapi.h>
|
||||
|
||||
#include <Dbt.h>
|
||||
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
|
||||
0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
|
||||
|
||||
#else
|
||||
//linux includes
|
||||
// unix, linux & OSX includes
|
||||
#include <errno.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
|
|
@ -21,590 +21,457 @@ GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
|
|||
#include <pwd.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/convenience.hpp>
|
||||
#include <boost/process.hpp>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
wxDEFINE_EVENT(EVT_REMOVABLE_DRIVE_EJECTED, RemovableDriveEjectEvent);
|
||||
wxDEFINE_EVENT(EVT_REMOVABLE_DRIVES_CHANGED, RemovableDrivesChangedEvent);
|
||||
|
||||
#if _WIN32
|
||||
/* currently not used, left for possible future use
|
||||
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
*/
|
||||
void RemovableDriveManager::search_for_drives()
|
||||
std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() const
|
||||
{
|
||||
m_current_drives.clear();
|
||||
//get logical drives flags by letter in alphabetical order
|
||||
DWORD drives_mask = GetLogicalDrives();
|
||||
for (size_t i = 0; i < 26; i++)
|
||||
{
|
||||
if(drives_mask & (1 << i))
|
||||
{
|
||||
std::string path (1,(char)('A' + i));
|
||||
path+=":";
|
||||
UINT drive_type = GetDriveTypeA(path.c_str());
|
||||
// Get logical drives flags by letter in alphabetical order.
|
||||
DWORD drives_mask = ::GetLogicalDrives();
|
||||
|
||||
// Allocate the buffers before the loop.
|
||||
std::wstring volume_name;
|
||||
std::wstring file_system_name;
|
||||
// Iterate the Windows drives from 'C' to 'Z'
|
||||
std::vector<DriveData> current_drives;
|
||||
// Skip A and B drives.
|
||||
drives_mask >>= 2;
|
||||
for (char drive = 'C'; drive <= 'Z'; ++ drive, drives_mask >>= 1)
|
||||
if (drives_mask & 1) {
|
||||
std::string path { drive, ':' };
|
||||
UINT drive_type = ::GetDriveTypeA(path.c_str());
|
||||
// DRIVE_REMOVABLE on W are sd cards and usb thumbnails (not usb harddrives)
|
||||
if (drive_type == DRIVE_REMOVABLE)
|
||||
{
|
||||
if (drive_type == DRIVE_REMOVABLE) {
|
||||
// get name of drive
|
||||
std::wstring wpath = boost::nowide::widen(path);
|
||||
std::wstring volume_name;
|
||||
volume_name.resize(1024);
|
||||
std::wstring file_system_name;
|
||||
file_system_name.resize(1024);
|
||||
LPWSTR lp_volume_name_buffer = new wchar_t;
|
||||
BOOL error = GetVolumeInformationW(wpath.c_str(), &volume_name[0], sizeof(volume_name), NULL, NULL, NULL, &file_system_name[0], sizeof(file_system_name));
|
||||
if(error != 0)
|
||||
{
|
||||
volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end());
|
||||
if (file_system_name != L"")
|
||||
{
|
||||
volume_name.resize(MAX_PATH + 1);
|
||||
file_system_name.resize(MAX_PATH + 1);
|
||||
BOOL error = ::GetVolumeInformationW(wpath.c_str(), volume_name.data(), sizeof(volume_name), nullptr, nullptr, nullptr, file_system_name.data(), sizeof(file_system_name));
|
||||
if (error != 0) {
|
||||
volume_name.erase(volume_name.begin() + wcslen(volume_name.c_str()), volume_name.end());
|
||||
if (! file_system_name.empty()) {
|
||||
ULARGE_INTEGER free_space;
|
||||
GetDiskFreeSpaceExA(path.c_str(), &free_space, NULL, NULL);
|
||||
if (free_space.QuadPart > 0)
|
||||
{
|
||||
::GetDiskFreeSpaceExW(wpath.c_str(), &free_space, nullptr, nullptr);
|
||||
if (free_space.QuadPart > 0) {
|
||||
path += "\\";
|
||||
m_current_drives.push_back(DriveData(boost::nowide::narrow(volume_name), path));
|
||||
current_drives.emplace_back(DriveData{ boost::nowide::narrow(volume_name), path });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return current_drives;
|
||||
}
|
||||
void RemovableDriveManager::eject_drive(const std::string &path)
|
||||
|
||||
// Called from UI therefore it blocks the UI thread.
|
||||
// It also blocks updates at the worker thread.
|
||||
// Win32 implementation.
|
||||
void RemovableDriveManager::eject_drive()
|
||||
{
|
||||
if(m_current_drives.empty())
|
||||
if (m_last_save_path.empty())
|
||||
return;
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if ((*it).path == path)
|
||||
{
|
||||
// get handle to device
|
||||
std::string mpath = "\\\\.\\" + path;
|
||||
mpath = mpath.substr(0, mpath.size() - 1);
|
||||
HANDLE handle = CreateFileA(mpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
|
||||
return;
|
||||
}
|
||||
DWORD deviceControlRetVal(0);
|
||||
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
|
||||
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
|
||||
|
||||
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
|
||||
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
if (error == 0)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
std::cerr << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||
if (it_drive_data != m_current_drives.end()) {
|
||||
// get handle to device
|
||||
std::string mpath = "\\\\.\\" + m_last_save_path;
|
||||
mpath = mpath.substr(0, mpath.size() - 1);
|
||||
HANDLE handle = CreateFileW(boost::nowide::widen(mpath).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
|
||||
return;
|
||||
}
|
||||
DWORD deviceControlRetVal(0);
|
||||
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
|
||||
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
|
||||
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
|
||||
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
if (error == 0) {
|
||||
CloseHandle(handle);
|
||||
m_did_eject = true;
|
||||
m_current_drives.erase(it);
|
||||
m_ejected_path = m_last_save_path;
|
||||
m_ejected_name = m_last_save_name;
|
||||
break;
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
|
||||
return;
|
||||
}
|
||||
CloseHandle(handle);
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true)));
|
||||
m_current_drives.erase(it_drive_data);
|
||||
}
|
||||
}
|
||||
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||
{
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
if (m_current_drives.empty())
|
||||
return false;
|
||||
return std::string();
|
||||
std::size_t found = path.find_last_of("\\");
|
||||
std::string new_path = path.substr(0, found);
|
||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
char drive = (*it).path[0];
|
||||
if (drive == ('A' + letter))
|
||||
return true;
|
||||
int letter = PathGetDriveNumberW(boost::nowide::widen(new_path).c_str());
|
||||
for (const DriveData &drive_data : m_current_drives) {
|
||||
char drive = drive_data.path[0];
|
||||
if (drive == 'A' + letter)
|
||||
return path;
|
||||
}
|
||||
return false;
|
||||
return m_current_drives.front().path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_from_path(const std::string& path)
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
std::size_t found = path.find_last_of("\\");
|
||||
std::string new_path = path.substr(0, found);
|
||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
char drive = (*it).path[0];
|
||||
if (drive == ('A' + letter))
|
||||
return (*it).path;
|
||||
int letter = PathGetDriveNumberW(boost::nowide::widen(new_path).c_str());
|
||||
for (const DriveData &drive_data : m_current_drives) {
|
||||
assert(! drive_data.path.empty());
|
||||
if (drive_data.path.front() == 'A' + letter)
|
||||
return drive_data.path;
|
||||
}
|
||||
return "";
|
||||
return std::string();
|
||||
}
|
||||
void RemovableDriveManager::register_window()
|
||||
{
|
||||
//creates new unvisible window that is recieving callbacks from system
|
||||
// structure to register
|
||||
/* currently not used, left for possible future use
|
||||
WNDCLASSEX wndClass;
|
||||
wndClass.cbSize = sizeof(WNDCLASSEX);
|
||||
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
|
||||
wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
|
||||
wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);//this is callback
|
||||
wndClass.cbClsExtra = 0;
|
||||
wndClass.cbWndExtra = 0;
|
||||
wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
|
||||
wndClass.hbrBackground = CreateSolidBrush(RGB(192, 192, 192));
|
||||
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
|
||||
wndClass.lpszClassName = L"PrusaSlicer_aux_class";
|
||||
wndClass.lpszMenuName = NULL;
|
||||
wndClass.hIconSm = wndClass.hIcon;
|
||||
if(!RegisterClassEx(&wndClass))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
return;
|
||||
}
|
||||
|
||||
HWND hWnd = CreateWindowEx(
|
||||
WS_EX_NOACTIVATE,
|
||||
L"PrusaSlicer_aux_class",
|
||||
L"PrusaSlicer_aux_wnd",
|
||||
WS_DISABLED, // style
|
||||
CW_USEDEFAULT, 0,
|
||||
640, 480,
|
||||
NULL, NULL,
|
||||
GetModuleHandle(NULL),
|
||||
NULL);
|
||||
if(hWnd == NULL)
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
// Called by Win32 Volume arrived / detached callback.
|
||||
void RemovableDriveManager::volumes_changed()
|
||||
{
|
||||
if (m_initialized) {
|
||||
// Signal the worker thread to wake up and enumerate removable drives.
|
||||
m_wakeup = true;
|
||||
m_thread_stop_condition.notify_all();
|
||||
}
|
||||
//ShowWindow(hWnd, SW_SHOWNORMAL);
|
||||
UpdateWindow(hWnd);
|
||||
*/
|
||||
}
|
||||
/* currently not used, left for possible future use
|
||||
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
#else
|
||||
|
||||
namespace search_for_drives_internal
|
||||
{
|
||||
// here we need to catch messeges about device removal
|
||||
// problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device.
|
||||
//uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates
|
||||
|
||||
LRESULT lRet = 1;
|
||||
static HDEVNOTIFY hDeviceNotify;
|
||||
|
||||
switch (message)
|
||||
static bool compare_filesystem_id(const std::string &path_a, const std::string &path_b)
|
||||
{
|
||||
case WM_CREATE:
|
||||
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
|
||||
struct stat buf;
|
||||
stat(path_a.c_str() ,&buf);
|
||||
dev_t id_a = buf.st_dev;
|
||||
stat(path_b.c_str() ,&buf);
|
||||
dev_t id_b = buf.st_dev;
|
||||
return id_a == id_b;
|
||||
}
|
||||
|
||||
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
|
||||
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
||||
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
NotificationFilter.dbcc_classguid = WceusbshGUID;
|
||||
|
||||
hDeviceNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
break;
|
||||
|
||||
case WM_DEVICECHANGE:
|
||||
void inspect_file(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out)
|
||||
{
|
||||
// here is the important
|
||||
if(wParam == DBT_DEVICEREMOVECOMPLETE)
|
||||
{
|
||||
- RemovableDriveManager::get_instance().update(0, true);
|
||||
//confirms if the file is removable drive and adds it to vector
|
||||
|
||||
//if not same file system - could be removable drive
|
||||
if (! compare_filesystem_id(path, parent_path)) {
|
||||
//free space
|
||||
boost::filesystem::space_info si = boost::filesystem::space(path);
|
||||
if (si.available != 0) {
|
||||
//user id
|
||||
struct stat buf;
|
||||
stat(path.c_str(), &buf);
|
||||
uid_t uid = buf.st_uid;
|
||||
std::string username(std::getenv("USER"));
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw != 0 && pw->pw_name == username)
|
||||
out.emplace_back(DriveData{ boost::filesystem::basename(boost::filesystem::path(path)), path });
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Send all other messages on to the default windows handler.
|
||||
lRet = DefWindowProc(hWnd, message, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
return lRet;
|
||||
|
||||
}
|
||||
*/
|
||||
#else
|
||||
void RemovableDriveManager::search_for_drives()
|
||||
{
|
||||
|
||||
m_current_drives.clear();
|
||||
|
||||
#if __APPLE__
|
||||
// if on macos obj-c class will enumerate
|
||||
if(m_rdmmm)
|
||||
{
|
||||
m_rdmmm->list_devices();
|
||||
}
|
||||
#else
|
||||
|
||||
static void search_path(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out)
|
||||
{
|
||||
glob_t globbuf;
|
||||
globbuf.gl_offs = 2;
|
||||
int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
|
||||
if (error == 0) {
|
||||
for (size_t i = 0; i < globbuf.gl_pathc; ++ i)
|
||||
inspect_file(globbuf.gl_pathv[i], parent_path, out);
|
||||
} else {
|
||||
//if error - path probably doesnt exists so function just exits
|
||||
//std::cout<<"glob error "<< error<< "\n";
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() const
|
||||
{
|
||||
std::vector<DriveData> current_drives;
|
||||
|
||||
#if __APPLE__
|
||||
|
||||
this->list_devices(current_drives);
|
||||
|
||||
#else
|
||||
|
||||
//search /media/* folder
|
||||
search_path("/media/*", "/media");
|
||||
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
|
||||
|
||||
//search_path("/Volumes/*", "/Volumes");
|
||||
std::string path(std::getenv("USER"));
|
||||
std::string pp(path);
|
||||
|
||||
{
|
||||
//search /media/USERNAME/* folder
|
||||
pp = "/media/"+pp;
|
||||
path = "/media/" + path + "/*";
|
||||
search_path(path, pp);
|
||||
//search /media/USERNAME/* folder
|
||||
pp = "/media/"+pp;
|
||||
path = "/media/" + path + "/*";
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
|
||||
//search /run/media/USERNAME/* folder
|
||||
path = "/run" + path;
|
||||
pp = "/run"+pp;
|
||||
search_path(path, pp);
|
||||
|
||||
}
|
||||
//search /run/media/USERNAME/* folder
|
||||
path = "/run" + path;
|
||||
pp = "/run"+pp;
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
#endif
|
||||
}
|
||||
void RemovableDriveManager::search_path(const std::string &path,const std::string &parent_path)
|
||||
{
|
||||
glob_t globbuf;
|
||||
globbuf.gl_offs = 2;
|
||||
int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
|
||||
if(error == 0)
|
||||
{
|
||||
for(size_t i = 0; i < globbuf.gl_pathc; i++)
|
||||
{
|
||||
inspect_file(globbuf.gl_pathv[i], parent_path);
|
||||
}
|
||||
}else
|
||||
{
|
||||
//if error - path probably doesnt exists so function just exits
|
||||
//std::cout<<"glob error "<< error<< "\n";
|
||||
}
|
||||
|
||||
globfree(&globbuf);
|
||||
}
|
||||
void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path)
|
||||
{
|
||||
//confirms if the file is removable drive and adds it to vector
|
||||
|
||||
//if not same file system - could be removable drive
|
||||
if(!compare_filesystem_id(path, parent_path))
|
||||
{
|
||||
//free space
|
||||
boost::filesystem::space_info si = boost::filesystem::space(path);
|
||||
if(si.available != 0)
|
||||
{
|
||||
//user id
|
||||
struct stat buf;
|
||||
stat(path.c_str(), &buf);
|
||||
uid_t uid = buf.st_uid;
|
||||
std::string username(std::getenv("USER"));
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw != 0 && pw->pw_name == username)
|
||||
m_current_drives.push_back(DriveData(boost::filesystem::basename(boost::filesystem::path(path)), path));
|
||||
}
|
||||
|
||||
}
|
||||
return current_drives;
|
||||
}
|
||||
bool RemovableDriveManager::compare_filesystem_id(const std::string &path_a, const std::string &path_b)
|
||||
|
||||
// Called from UI therefore it blocks the UI thread.
|
||||
// It also blocks updates at the worker thread.
|
||||
// Unix & OSX implementation.
|
||||
void RemovableDriveManager::eject_drive()
|
||||
{
|
||||
struct stat buf;
|
||||
stat(path_a.c_str() ,&buf);
|
||||
dev_t id_a = buf.st_dev;
|
||||
stat(path_b.c_str() ,&buf);
|
||||
dev_t id_b = buf.st_dev;
|
||||
return id_a == id_b;
|
||||
}
|
||||
void RemovableDriveManager::eject_drive(const std::string &path)
|
||||
{
|
||||
if (m_current_drives.empty())
|
||||
if (m_last_save_path.empty())
|
||||
return;
|
||||
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if((*it).path == path)
|
||||
{
|
||||
|
||||
std::string correct_path(path);
|
||||
for (size_t i = 0; i < correct_path.size(); ++i)
|
||||
{
|
||||
if(correct_path[i]==' ')
|
||||
{
|
||||
correct_path = correct_path.insert(i,1,'\\');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
|
||||
// there is no usable command in c++ so terminal command is used instead
|
||||
// but neither triggers "succesful safe removal messege"
|
||||
std::string command = "";
|
||||
#if __APPLE__
|
||||
//m_rdmmm->eject_device(path);
|
||||
command = "diskutil unmount ";
|
||||
#else
|
||||
command = "umount ";
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||
if (it_drive_data != m_current_drives.end()) {
|
||||
std::string correct_path(m_last_save_path);
|
||||
#ifndef __APPLE__
|
||||
for (size_t i = 0; i < correct_path.size(); ++i)
|
||||
if (correct_path[i]==' ') {
|
||||
correct_path = correct_path.insert(i,1,'\\');
|
||||
++ i;
|
||||
}
|
||||
#endif
|
||||
command += correct_path;
|
||||
int err = system(command.c_str());
|
||||
if(err)
|
||||
{
|
||||
std::cerr<<"Ejecting failed\n";
|
||||
return;
|
||||
}
|
||||
|
||||
m_did_eject = true;
|
||||
m_current_drives.erase(it);
|
||||
m_ejected_path = m_last_save_path;
|
||||
m_ejected_name = m_last_save_name;
|
||||
break;
|
||||
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
|
||||
// there is no usable command in c++ so terminal command is used instead
|
||||
// but neither triggers "succesful safe removal messege"
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting started";
|
||||
boost::process::ipstream istd_err;
|
||||
boost::process::child child(
|
||||
#if __APPLE__
|
||||
boost::process::search_path("diskutil"), "eject", correct_path.c_str(), (boost::process::std_out & boost::process::std_err) > istd_err);
|
||||
//Another option how to eject at mac. Currently not working.
|
||||
//used insted of system() command;
|
||||
//this->eject_device(correct_path);
|
||||
#else
|
||||
boost::process::search_path("umount"), correct_path.c_str(), (boost::process::std_out & boost::process::std_err) > istd_err);
|
||||
#endif
|
||||
std::string line;
|
||||
while (child.running() && std::getline(istd_err, line)) {
|
||||
BOOST_LOG_TRIVIAL(trace) << line;
|
||||
}
|
||||
// wait for command to finnish (blocks ui thread)
|
||||
child.wait();
|
||||
int err = child.exit_code();
|
||||
if (err) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting failed";
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
|
||||
return;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
|
||||
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(*it_drive_data), true)));
|
||||
m_current_drives.erase(it_drive_data);
|
||||
}
|
||||
|
||||
}
|
||||
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||
{
|
||||
if (m_current_drives.empty())
|
||||
return false;
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
std::size_t found = path.find_last_of("/");
|
||||
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if(compare_filesystem_id(new_path, (*it).path))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
for (const DriveData &data : m_current_drives)
|
||||
if (search_for_drives_internal::compare_filesystem_id(new_path, data.path))
|
||||
return path;
|
||||
return m_current_drives.empty() ? std::string() : m_current_drives.front().path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_from_path(const std::string& path)
|
||||
{
|
||||
std::size_t found = path.find_last_of("/");
|
||||
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
||||
|
||||
// trim the filename
|
||||
found = new_path.find_last_of("/");
|
||||
new_path = new_path.substr(0, found);
|
||||
|
||||
//check if same filesystem
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if (compare_filesystem_id(new_path, (*it).path))
|
||||
return (*it).path;
|
||||
}
|
||||
return "";
|
||||
// check if same filesystem
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
for (const DriveData &drive_data : m_current_drives)
|
||||
if (search_for_drives_internal::compare_filesystem_id(new_path, drive_data.path))
|
||||
return drive_data.path;
|
||||
return std::string();
|
||||
}
|
||||
#endif
|
||||
|
||||
RemovableDriveManager::RemovableDriveManager():
|
||||
m_drives_count(0),
|
||||
m_last_update(0),
|
||||
m_last_save_path(""),
|
||||
m_last_save_name(""),
|
||||
m_last_save_path_verified(false),
|
||||
m_is_writing(false),
|
||||
m_did_eject(false),
|
||||
m_plater_ready_to_slice(true),
|
||||
m_ejected_path(""),
|
||||
m_ejected_name("")
|
||||
void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
|
||||
{
|
||||
assert(! m_initialized);
|
||||
assert(m_callback_evt_handler == nullptr);
|
||||
|
||||
if (m_initialized)
|
||||
return;
|
||||
|
||||
m_initialized = true;
|
||||
m_callback_evt_handler = callback_evt_handler;
|
||||
|
||||
#if __APPLE__
|
||||
, m_rdmmm(new RDMMMWrapper())
|
||||
this->register_window_osx();
|
||||
#endif
|
||||
{}
|
||||
RemovableDriveManager::~RemovableDriveManager()
|
||||
{
|
||||
#if __APPLE__
|
||||
delete m_rdmmm;
|
||||
#endif
|
||||
}
|
||||
void RemovableDriveManager::init()
|
||||
{
|
||||
//add_callback([](void) { RemovableDriveManager::get_instance().print(); });
|
||||
#if _WIN32
|
||||
//register_window();
|
||||
#elif __APPLE__
|
||||
m_rdmmm->register_window();
|
||||
#endif
|
||||
update(0, true);
|
||||
}
|
||||
bool RemovableDriveManager::update(const long time,const bool check)
|
||||
{
|
||||
if(time != 0) //time = 0 is forced update
|
||||
{
|
||||
long diff = m_last_update - time;
|
||||
if(diff <= -2)
|
||||
{
|
||||
m_last_update = time;
|
||||
}else
|
||||
{
|
||||
return false; // return value shouldnt matter if update didnt run
|
||||
}
|
||||
}
|
||||
search_for_drives();
|
||||
if (m_drives_count != m_current_drives.size())
|
||||
{
|
||||
if (check)
|
||||
{
|
||||
check_and_notify();
|
||||
}
|
||||
m_drives_count = m_current_drives.size();
|
||||
}
|
||||
return !m_current_drives.empty();
|
||||
|
||||
#ifdef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#else // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
// Don't call update() manually, as the UI triggered APIs call this->update() anyways.
|
||||
m_thread = boost::thread((boost::bind(&RemovableDriveManager::thread_proc, this)));
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
}
|
||||
|
||||
bool RemovableDriveManager::is_drive_mounted(const std::string &path) const
|
||||
void RemovableDriveManager::shutdown()
|
||||
{
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if ((*it).path == path)
|
||||
#if __APPLE__
|
||||
this->unregister_window_osx();
|
||||
#endif
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
if (m_thread.joinable()) {
|
||||
// Stop the worker thread, if running.
|
||||
{
|
||||
return true;
|
||||
// Notify the worker thread to cancel wait on detection polling.
|
||||
std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
|
||||
m_stop = true;
|
||||
}
|
||||
m_thread_stop_condition.notify_all();
|
||||
// Wait for the worker thread to stop.
|
||||
m_thread.join();
|
||||
m_stop = false;
|
||||
}
|
||||
return false;
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
m_initialized = false;
|
||||
m_callback_evt_handler = nullptr;
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_path()
|
||||
|
||||
bool RemovableDriveManager::set_and_verify_last_save_path(const std::string &path)
|
||||
{
|
||||
if (m_current_drives.size() == 0)
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
m_last_save_path = this->get_removable_drive_from_path(path);
|
||||
return ! m_last_save_path.empty();
|
||||
}
|
||||
|
||||
RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
|
||||
{
|
||||
|
||||
RemovableDriveManager::RemovableDrivesStatus out;
|
||||
{
|
||||
reset_last_save_path();
|
||||
return "";
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
out.has_eject = this->find_last_save_path_drive_data() != m_current_drives.end();
|
||||
out.has_removable_drives = ! m_current_drives.empty();
|
||||
}
|
||||
if (m_last_save_path_verified)
|
||||
return m_last_save_path;
|
||||
return m_current_drives.back().path;
|
||||
if (! out.has_eject)
|
||||
m_last_save_path.clear();
|
||||
return out;
|
||||
}
|
||||
std::string RemovableDriveManager::get_last_save_path() const
|
||||
|
||||
// Update is called from thread_proc() and from most of the public methods on demand.
|
||||
void RemovableDriveManager::update()
|
||||
{
|
||||
if (!m_last_save_path_verified)
|
||||
return "";
|
||||
return m_last_save_path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_last_save_name() const
|
||||
{
|
||||
return m_last_save_name;
|
||||
}
|
||||
std::vector<DriveData> RemovableDriveManager::get_all_drives() const
|
||||
{
|
||||
return m_current_drives;
|
||||
}
|
||||
void RemovableDriveManager::check_and_notify()
|
||||
{
|
||||
if(m_drive_count_changed_callback)
|
||||
{
|
||||
m_drive_count_changed_callback(m_plater_ready_to_slice);
|
||||
}
|
||||
if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && !is_drive_mounted(m_last_save_path))
|
||||
{
|
||||
for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
|
||||
{
|
||||
(*it)();
|
||||
tbb::mutex::scoped_lock inside_update_lock;
|
||||
#ifdef _WIN32
|
||||
// All wake up calls up to now are now consumed when the drive enumeration starts.
|
||||
m_wakeup = false;
|
||||
#endif // _WIN32
|
||||
if (inside_update_lock.try_acquire(m_inside_update_mutex)) {
|
||||
// Got the lock without waiting. That means, the update was not running.
|
||||
// Run the update.
|
||||
std::vector<DriveData> current_drives = this->search_for_removable_drives();
|
||||
// Post update events.
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
std::sort(current_drives.begin(), current_drives.end());
|
||||
if (current_drives != m_current_drives) {
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDrivesChangedEvent(EVT_REMOVABLE_DRIVES_CHANGED));
|
||||
}
|
||||
m_current_drives = std::move(current_drives);
|
||||
} else {
|
||||
// Acquiring the m_iniside_update lock failed, therefore another update is running.
|
||||
// Just block until the other instance of update() finishes.
|
||||
inside_update_lock.acquire(m_inside_update_mutex);
|
||||
}
|
||||
}
|
||||
void RemovableDriveManager::add_remove_callback(std::function<void()> callback)
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
void RemovableDriveManager::thread_proc()
|
||||
{
|
||||
m_callbacks.push_back(callback);
|
||||
}
|
||||
void RemovableDriveManager::erase_callbacks()
|
||||
{
|
||||
m_callbacks.clear();
|
||||
}
|
||||
void RemovableDriveManager::set_drive_count_changed_callback(std::function<void(const bool)> callback)
|
||||
{
|
||||
m_drive_count_changed_callback = callback;
|
||||
}
|
||||
void RemovableDriveManager::set_plater_ready_to_slice(bool b)
|
||||
{
|
||||
m_plater_ready_to_slice = b;
|
||||
}
|
||||
void RemovableDriveManager::set_last_save_path(const std::string& path)
|
||||
{
|
||||
if(m_last_save_path_verified)// if old path is on drive
|
||||
{
|
||||
if(get_drive_from_path(path) != "") //and new is too, rewrite the path
|
||||
// Signal the worker thread to update initially.
|
||||
#ifdef _WIN32
|
||||
m_wakeup = true;
|
||||
#endif // _WIN32
|
||||
|
||||
for (;;) {
|
||||
// Wait for 2 seconds before running the disk enumeration.
|
||||
// Cancellable.
|
||||
{
|
||||
m_last_save_path_verified = false;
|
||||
m_last_save_path = path;
|
||||
}//else do nothing
|
||||
}else
|
||||
{
|
||||
m_last_save_path = path;
|
||||
}
|
||||
}
|
||||
void RemovableDriveManager::verify_last_save_path()
|
||||
{
|
||||
std::string last_drive = get_drive_from_path(m_last_save_path);
|
||||
if (last_drive != "")
|
||||
{
|
||||
m_last_save_path_verified = true;
|
||||
m_last_save_path = last_drive;
|
||||
m_last_save_name = get_drive_name(last_drive);
|
||||
}else
|
||||
{
|
||||
reset_last_save_path();
|
||||
}
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_name(const std::string& path) const
|
||||
{
|
||||
if (m_current_drives.size() == 0)
|
||||
return "";
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if ((*it).path == path)
|
||||
{
|
||||
return (*it).name;
|
||||
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
|
||||
#ifdef _WIN32
|
||||
// Reacting to updates by WM_DEVICECHANGE and WM_USER_MEDIACHANGED
|
||||
m_thread_stop_condition.wait(lck, [this]{ return m_stop || m_wakeup; });
|
||||
#else
|
||||
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(2), [this]{ return m_stop; });
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
bool RemovableDriveManager::is_last_drive_removed()
|
||||
{
|
||||
if(!m_last_save_path_verified)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool r = !is_drive_mounted(m_last_save_path);
|
||||
if (r)
|
||||
{
|
||||
reset_last_save_path();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
bool RemovableDriveManager::is_last_drive_removed_with_update(const long time)
|
||||
{
|
||||
update(time, false);
|
||||
return is_last_drive_removed();
|
||||
}
|
||||
void RemovableDriveManager::reset_last_save_path()
|
||||
{
|
||||
m_last_save_path_verified = false;
|
||||
m_last_save_path = "";
|
||||
m_last_save_name = "";
|
||||
}
|
||||
void RemovableDriveManager::set_is_writing(const bool b)
|
||||
{
|
||||
m_is_writing = b;
|
||||
if (b)
|
||||
{
|
||||
m_did_eject = false;
|
||||
if (m_stop)
|
||||
// Stop the worker thread.
|
||||
break;
|
||||
// Update m_current drives and send out update events.
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
bool RemovableDriveManager::get_is_writing() const
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
std::vector<DriveData>::const_iterator RemovableDriveManager::find_last_save_path_drive_data() const
|
||||
{
|
||||
return m_is_writing;
|
||||
return Slic3r::binary_find_by_predicate(m_current_drives.begin(), m_current_drives.end(),
|
||||
[this](const DriveData &data){ return data.path < m_last_save_path; },
|
||||
[this](const DriveData &data){ return data.path == m_last_save_path; });
|
||||
}
|
||||
bool RemovableDriveManager::get_did_eject() const
|
||||
{
|
||||
return m_did_eject;
|
||||
}
|
||||
void RemovableDriveManager::set_did_eject(const bool b)
|
||||
{
|
||||
m_did_eject = b;
|
||||
}
|
||||
size_t RemovableDriveManager::get_drives_count() const
|
||||
{
|
||||
return m_current_drives.size();
|
||||
}
|
||||
std::string RemovableDriveManager::get_ejected_path() const
|
||||
{
|
||||
return m_ejected_path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_ejected_name() const
|
||||
{
|
||||
return m_ejected_name;
|
||||
}
|
||||
}}//namespace Slicer::Gui
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
|||
|
|
@ -3,119 +3,136 @@
|
|||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <tbb/mutex.h>
|
||||
#include <condition_variable>
|
||||
|
||||
// Custom wxWidget events
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
#if __APPLE__
|
||||
class RDMMMWrapper;
|
||||
#endif
|
||||
|
||||
|
||||
struct DriveData
|
||||
{
|
||||
std::string name;
|
||||
std::string path;
|
||||
DriveData(std::string n, std::string p):name(n),path(p){}
|
||||
|
||||
void clear() {
|
||||
name.clear();
|
||||
path.clear();
|
||||
}
|
||||
bool empty() const {
|
||||
return path.empty();
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator< (const DriveData &lhs, const DriveData &rhs) { return lhs.path < rhs.path; }
|
||||
inline bool operator> (const DriveData &lhs, const DriveData &rhs) { return lhs.path > rhs.path; }
|
||||
inline bool operator==(const DriveData &lhs, const DriveData &rhs) { return lhs.path == rhs.path; }
|
||||
|
||||
using RemovableDriveEjectEvent = Event<std::pair<DriveData, bool>>;
|
||||
wxDECLARE_EVENT(EVT_REMOVABLE_DRIVE_EJECTED, RemovableDriveEjectEvent);
|
||||
|
||||
using RemovableDrivesChangedEvent = SimpleEvent;
|
||||
wxDECLARE_EVENT(EVT_REMOVABLE_DRIVES_CHANGED, RemovableDrivesChangedEvent);
|
||||
|
||||
#if __APPLE__
|
||||
// Callbacks on device plug / unplug work reliably on OSX.
|
||||
#define REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
#endif // __APPLE__
|
||||
|
||||
class RemovableDriveManager
|
||||
{
|
||||
#if __APPLE__
|
||||
friend class RDMMMWrapper;
|
||||
#endif
|
||||
public:
|
||||
static RemovableDriveManager& get_instance()
|
||||
{
|
||||
static RemovableDriveManager instance;
|
||||
return instance;
|
||||
}
|
||||
RemovableDriveManager() = default;
|
||||
RemovableDriveManager(RemovableDriveManager const&) = delete;
|
||||
void operator=(RemovableDriveManager const&) = delete;
|
||||
~RemovableDriveManager();
|
||||
//call only once. on apple register for unmnount callbacks. on windows register for device notification is prepared but not called (eject usb drive on widnows doesnt trigger the callback, sdc ard does), also enumerates devices for first time so init shoud be called on linux too.
|
||||
void init();
|
||||
//update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime()
|
||||
bool update(const long time = 0,const bool check = false);
|
||||
bool is_drive_mounted(const std::string &path) const;
|
||||
void eject_drive(const std::string &path);
|
||||
//returns path to last drive which was used, if none was used, returns device that was enumerated last
|
||||
std::string get_last_save_path() const;
|
||||
std::string get_last_save_name() const;
|
||||
//returns path to last drive which was used, if none was used, returns empty string
|
||||
std::string get_drive_path();
|
||||
std::vector<DriveData> get_all_drives() const;
|
||||
bool is_path_on_removable_drive(const std::string &path);
|
||||
// callback will notify only if device with last save path was removed
|
||||
void add_remove_callback(std::function<void()> callback);
|
||||
// erases all remove callbacks added by add_remove_callback()
|
||||
void erase_callbacks();
|
||||
//drive_count_changed callback is called on every added or removed device
|
||||
void set_drive_count_changed_callback(std::function<void(const bool)> callback);
|
||||
//thi serves to set correct value for drive_count_changed callback
|
||||
void set_plater_ready_to_slice(bool b);
|
||||
// marks one of the eveices in vector as last used
|
||||
void set_last_save_path(const std::string &path);
|
||||
void verify_last_save_path();
|
||||
bool is_last_drive_removed();
|
||||
// param as update()
|
||||
bool is_last_drive_removed_with_update(const long time = 0);
|
||||
void set_is_writing(const bool b);
|
||||
bool get_is_writing() const;
|
||||
bool get_did_eject() const;
|
||||
void set_did_eject(const bool b);
|
||||
std::string get_drive_name(const std::string& path) const;
|
||||
size_t get_drives_count() const;
|
||||
std::string get_ejected_path() const;
|
||||
std::string get_ejected_name() const;
|
||||
~RemovableDriveManager() { assert(! m_initialized); }
|
||||
|
||||
// Start the background thread and register this window as a target for update events.
|
||||
// Register for OSX notifications.
|
||||
void init(wxEvtHandler *callback_evt_handler);
|
||||
// Stop the background thread of the removable drive manager, so that no new updates will be sent out.
|
||||
// Deregister OSX notifications.
|
||||
void shutdown();
|
||||
|
||||
// Returns path to a removable media if it exists, prefering the input path.
|
||||
std::string get_removable_drive_path(const std::string &path);
|
||||
bool is_path_on_removable_drive(const std::string &path) { return this->get_removable_drive_path(path) == path; }
|
||||
|
||||
// Verify whether the path provided is on removable media. If so, save the path for further eject and return true, otherwise return false.
|
||||
bool set_and_verify_last_save_path(const std::string &path);
|
||||
// Eject drive of a file set by set_and_verify_last_save_path().
|
||||
// On Unix / OSX, the function blocks and sends out the EVT_REMOVABLE_DRIVE_EJECTED event on success.
|
||||
// On Windows, the function does not block, and the eject is detected in the background thread.
|
||||
void eject_drive();
|
||||
|
||||
// Status is used to retrieve info for showing UI buttons.
|
||||
// Status is called every time when change of UI buttons is possible therefore should not perform update.
|
||||
struct RemovableDrivesStatus {
|
||||
bool has_removable_drives { false };
|
||||
bool has_eject { false };
|
||||
};
|
||||
RemovableDrivesStatus status();
|
||||
|
||||
// Enumerates current drives and sends out wxWidget events on change or eject.
|
||||
// Called by each public method, by the background thread and from RemovableDriveManagerMM::on_device_unmount OSX notification handler.
|
||||
// Not to be called manually.
|
||||
// Public to be accessible from RemovableDriveManagerMM::on_device_unmount OSX notification handler.
|
||||
// It would be better to make this method private and friend to RemovableDriveManagerMM, but RemovableDriveManagerMM is an ObjectiveC class.
|
||||
void update();
|
||||
|
||||
#ifdef _WIN32
|
||||
// Called by Win32 Volume arrived / detached callback.
|
||||
void volumes_changed();
|
||||
#endif // _WIN32
|
||||
|
||||
private:
|
||||
RemovableDriveManager();
|
||||
void search_for_drives();
|
||||
//triggers callbacks if last used drive was removed
|
||||
void check_and_notify();
|
||||
//returns drive path (same as path in DriveData) if exists otherwise empty string ""
|
||||
std::string get_drive_from_path(const std::string& path);
|
||||
void reset_last_save_path();
|
||||
bool m_initialized { false };
|
||||
wxEvtHandler* m_callback_evt_handler { nullptr };
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
// Worker thread, worker thread synchronization and callbacks to the UI thread.
|
||||
void thread_proc();
|
||||
boost::thread m_thread;
|
||||
std::condition_variable m_thread_stop_condition;
|
||||
mutable std::mutex m_thread_stop_mutex;
|
||||
bool m_stop { false };
|
||||
#ifdef _WIN32
|
||||
std::atomic<bool> m_wakeup { false };
|
||||
#endif /* _WIN32 */
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
// Called from update() to enumerate removable drives.
|
||||
std::vector<DriveData> search_for_removable_drives() const;
|
||||
|
||||
// m_current_drives is guarded by m_drives_mutex
|
||||
// sorted ascending by path
|
||||
std::vector<DriveData> m_current_drives;
|
||||
mutable tbb::mutex m_drives_mutex;
|
||||
// Locking the update() function to avoid that the function is executed multiple times.
|
||||
mutable tbb::mutex m_inside_update_mutex;
|
||||
|
||||
// Returns drive path (same as path in DriveData) if exists otherwise empty string.
|
||||
std::string get_removable_drive_from_path(const std::string& path);
|
||||
// Returns iterator to a drive in m_current_drives with path equal to m_last_save_path or end().
|
||||
std::vector<DriveData>::const_iterator find_last_save_path_drive_data() const;
|
||||
// Set with set_and_verify_last_save_path() to a removable drive path to be ejected.
|
||||
std::string m_last_save_path;
|
||||
|
||||
std::vector<DriveData> m_current_drives;
|
||||
std::vector<std::function<void()>> m_callbacks;
|
||||
std::function<void(const bool)> m_drive_count_changed_callback;
|
||||
size_t m_drives_count;
|
||||
long m_last_update;
|
||||
std::string m_last_save_path;
|
||||
bool m_last_save_path_verified;
|
||||
std::string m_last_save_name;
|
||||
bool m_is_writing;//on device
|
||||
bool m_did_eject;
|
||||
bool m_plater_ready_to_slice;
|
||||
std::string m_ejected_path;
|
||||
std::string m_ejected_name;
|
||||
#if _WIN32
|
||||
//registers for notifications by creating invisible window
|
||||
void register_window();
|
||||
#else
|
||||
#if __APPLE__
|
||||
RDMMMWrapper * m_rdmmm;
|
||||
#endif
|
||||
void search_path(const std::string &path, const std::string &parent_path);
|
||||
bool compare_filesystem_id(const std::string &path_a, const std::string &path_b);
|
||||
void inspect_file(const std::string &path, const std::string &parent_path);
|
||||
#endif
|
||||
};
|
||||
// apple wrapper for RemovableDriveManagerMM which searches for drives and/or ejects them
|
||||
#if __APPLE__
|
||||
class RDMMMWrapper
|
||||
{
|
||||
public:
|
||||
RDMMMWrapper();
|
||||
~RDMMMWrapper();
|
||||
void register_window();
|
||||
void list_devices();
|
||||
void register_window_osx();
|
||||
void unregister_window_osx();
|
||||
void list_devices(std::vector<DriveData> &out) const;
|
||||
// not used as of now
|
||||
void eject_device(const std::string &path);
|
||||
void log(const std::string &msg);
|
||||
protected:
|
||||
void *m_imp;
|
||||
//friend void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path);
|
||||
// Opaque pointer to RemovableDriveManagerMM
|
||||
void *m_impl_osx;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
}}
|
||||
#endif
|
||||
|
||||
}}
|
||||
|
||||
#endif // slic3r_GUI_RemovableDriveManager_hpp_
|
||||
|
|
|
|||
|
|
@ -1,31 +1,45 @@
|
|||
#import "RemovableDriveManager.hpp"
|
||||
#import "RemovableDriveManagerMM.h"
|
||||
#import "GUI_App.hpp"
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <DiskArbitration/DiskArbitration.h>
|
||||
|
||||
static void eject_callback(DADiskRef disk, DADissenterRef dissenter, void *context)
|
||||
{
|
||||
NSLog(@"eject successfull");
|
||||
}
|
||||
|
||||
static void unmount_callback(DADiskRef disk, DADissenterRef dissenter, void *context)
|
||||
{
|
||||
//if (dissenter) {
|
||||
//?
|
||||
//} else {
|
||||
DADiskEject(disk, kDADiskEjectOptionDefault, eject_callback, context);
|
||||
//}
|
||||
}
|
||||
|
||||
@implementation RemovableDriveManagerMM
|
||||
|
||||
|
||||
|
||||
-(instancetype) init
|
||||
{
|
||||
self = [super init];
|
||||
if(self)
|
||||
{
|
||||
}
|
||||
//if(self){}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) on_device_unmount: (NSNotification*) notification
|
||||
{
|
||||
NSLog(@"on device change");
|
||||
Slic3r::GUI::RemovableDriveManager::get_instance().update(0,true);
|
||||
//NSLog(@"on device change");
|
||||
Slic3r::GUI::wxGetApp().removable_drive_manager()->update();
|
||||
}
|
||||
|
||||
-(void) add_unmount_observer
|
||||
{
|
||||
NSLog(@"add unmount observer");
|
||||
//NSLog(@"add unmount observer");
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidUnmountNotification object:nil];
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidMountNotification object:nil];
|
||||
}
|
||||
|
||||
-(NSArray*) list_dev
|
||||
{
|
||||
// DEPRICATED:
|
||||
|
|
@ -40,118 +54,110 @@
|
|||
DADiskRef disk;
|
||||
DASessionRef session;
|
||||
CFDictionaryRef descDict;
|
||||
session = DASessionCreate(NULL);
|
||||
if (session == NULL) {
|
||||
session = DASessionCreate(nullptr);
|
||||
if (session == nullptr)
|
||||
err = EINVAL;
|
||||
}
|
||||
if (err == 0) {
|
||||
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)volURL);
|
||||
if (session == NULL) {
|
||||
disk = DADiskCreateFromVolumePath(nullptr,session,(CFURLRef)volURL);
|
||||
if (session == nullptr)
|
||||
err = EINVAL;
|
||||
}
|
||||
}
|
||||
if (err == 0) {
|
||||
descDict = DADiskCopyDescription(disk);
|
||||
if (descDict == NULL) {
|
||||
if (descDict == nullptr)
|
||||
err = EINVAL;
|
||||
}
|
||||
}
|
||||
if (err == 0) {
|
||||
CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
|
||||
BOOL ejectable = [mediaEjectableKey boolValue];
|
||||
CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
|
||||
|
||||
CFTypeRef deviceModelKey = CFDictionaryGetValue(descDict, kDADiskDescriptionDeviceModelKey);
|
||||
if (mediaEjectableKey != NULL)
|
||||
{
|
||||
BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")));
|
||||
//debug logging
|
||||
/*
|
||||
if (deviceProtocolName)
|
||||
NSLog(@"%@",(CFStringRef)deviceProtocolName);
|
||||
if (deviceModelKey)
|
||||
NSLog(@"-%@",(CFStringRef)deviceModelKey);
|
||||
*/
|
||||
if (mediaEjectableKey != nullptr) {
|
||||
BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")) || CFEqual(deviceProtocolName, CFSTR("Secure Digital")));
|
||||
//!CFEqual(deviceModelKey, CFSTR("Disk Image"));
|
||||
//
|
||||
if (op) {
|
||||
if (op)
|
||||
[result addObject:volURL.path];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (descDict != NULL) {
|
||||
if (descDict != nullptr)
|
||||
CFRelease(descDict);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//this eject drive is not used now
|
||||
-(void)eject_drive:(NSString *)path
|
||||
{
|
||||
DADiskRef disk;
|
||||
DASessionRef session;
|
||||
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
|
||||
int err = 0;
|
||||
session = DASessionCreate(NULL);
|
||||
if (session == NULL) {
|
||||
session = DASessionCreate(nullptr);
|
||||
if (session == nullptr)
|
||||
err = EINVAL;
|
||||
}
|
||||
if (err == 0) {
|
||||
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)url);
|
||||
}
|
||||
if (err == 0)
|
||||
disk = DADiskCreateFromVolumePath(nullptr,session,(CFURLRef)url);
|
||||
if( err == 0)
|
||||
{
|
||||
DADiskUnmount(disk, kDADiskUnmountOptionDefault,
|
||||
NULL, NULL);
|
||||
}
|
||||
if (disk != NULL) {
|
||||
//DADiskUnmount(disk, kDADiskUnmountOptionDefault, nullptr, nullptr);
|
||||
DADiskUnmount(disk, kDADiskUnmountOptionWhole | kDADiskUnmountOptionForce, unmount_callback, nullptr);
|
||||
if (disk != nullptr)
|
||||
CFRelease(disk);
|
||||
}
|
||||
if (session != NULL) {
|
||||
if (session != nullptr)
|
||||
CFRelease(session);
|
||||
}
|
||||
}
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
RDMMMWrapper::RDMMMWrapper():m_imp(nullptr){
|
||||
m_imp = [[RemovableDriveManagerMM alloc] init];
|
||||
}
|
||||
RDMMMWrapper::~RDMMMWrapper()
|
||||
{
|
||||
if(m_imp)
|
||||
{
|
||||
[m_imp release];
|
||||
}
|
||||
}
|
||||
void RDMMMWrapper::register_window()
|
||||
{
|
||||
if(m_imp)
|
||||
{
|
||||
[m_imp add_unmount_observer];
|
||||
}
|
||||
}
|
||||
void RDMMMWrapper::list_devices()
|
||||
{
|
||||
if(m_imp)
|
||||
{
|
||||
NSArray* devices = [m_imp list_dev];
|
||||
for (NSString* volumePath in devices)
|
||||
{
|
||||
NSLog(@"%@", volumePath);
|
||||
Slic3r::GUI::RemovableDriveManager::get_instance().inspect_file(std::string([volumePath UTF8String]), "/Volumes");
|
||||
}
|
||||
}
|
||||
}
|
||||
void RDMMMWrapper::log(const std::string &msg)
|
||||
{
|
||||
NSLog(@"%s", msg.c_str());
|
||||
}
|
||||
void RDMMMWrapper::eject_device(const std::string &path)
|
||||
{
|
||||
if(m_imp)
|
||||
{
|
||||
NSString * pth = [NSString stringWithCString:path.c_str()
|
||||
encoding:[NSString defaultCStringEncoding]];
|
||||
[m_imp eject_drive:pth];
|
||||
}
|
||||
}
|
||||
}}//namespace Slicer::GUI
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void RemovableDriveManager::register_window_osx()
|
||||
{
|
||||
assert(m_impl_osx == nullptr);
|
||||
m_impl_osx = [[RemovableDriveManagerMM alloc] init];
|
||||
if (m_impl_osx)
|
||||
[m_impl_osx add_unmount_observer];
|
||||
}
|
||||
|
||||
void RemovableDriveManager::unregister_window_osx()
|
||||
{
|
||||
if (m_impl_osx) {
|
||||
[m_impl_osx release];
|
||||
m_impl_osx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace search_for_drives_internal
|
||||
{
|
||||
void inspect_file(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out);
|
||||
}
|
||||
|
||||
void RemovableDriveManager::list_devices(std::vector<DriveData> &out) const
|
||||
{
|
||||
assert(m_impl_osx != nullptr);
|
||||
if (m_impl_osx) {
|
||||
NSArray* devices = [m_impl_osx list_dev];
|
||||
for (NSString* volumePath in devices)
|
||||
search_for_drives_internal::inspect_file(std::string([volumePath UTF8String]), "/Volumes", out);
|
||||
}
|
||||
}
|
||||
|
||||
// not used as of now
|
||||
void RemovableDriveManager::eject_device(const std::string &path)
|
||||
{
|
||||
assert(m_impl_osx != nullptr);
|
||||
if (m_impl_osx) {
|
||||
NSString * pth = [NSString stringWithCString:path.c_str() encoding:[NSString defaultCStringEncoding]];
|
||||
[m_impl_osx eject_drive:pth];
|
||||
}
|
||||
}
|
||||
|
||||
}}//namespace Slicer::GUI
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "Gizmos/GLGizmoBase.hpp"
|
||||
|
|
@ -439,6 +440,12 @@ void Selection::clear()
|
|||
update_type();
|
||||
this->set_bounding_boxes_dirty();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
// this happens while the application is closing
|
||||
if (wxGetApp().obj_manipul() == nullptr)
|
||||
return;
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
// resets the cache in the sidebar
|
||||
wxGetApp().obj_manipul()->reset_cache();
|
||||
|
||||
|
|
@ -1470,7 +1477,7 @@ void Selection::toggle_instance_printable_state()
|
|||
ModelInstance* instance = model_object->instances[instance_idx];
|
||||
const bool printable = !instance->printable;
|
||||
|
||||
wxString snapshot_text = model_object->instances.size() == 1 ? wxString::FromUTF8((boost::format("%1% %2%")
|
||||
wxString snapshot_text = model_object->instances.size() == 1 ? from_u8((boost::format("%1% %2%")
|
||||
% (printable ? _utf8(L("Set Printable")) : _utf8(L("Set Unprintable")))
|
||||
% model_object->name).str()) :
|
||||
(printable ? _(L("Set Printable Instance")) : _(L("Set Unprintable Instance")));
|
||||
|
|
|
|||
|
|
@ -145,7 +145,11 @@ SysInfoDialog::SysInfoDialog()
|
|||
"</font>"
|
||||
"</body>"
|
||||
"</html>", bgr_clr_str, text_clr_str, text_clr_str,
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
get_mem_info(true) + "<br>" + wxGetApp().get_gl_info(true, true));
|
||||
#else
|
||||
get_mem_info(true) + "<br>" + _3DScene::get_gl_info(true, true));
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
m_opengl_info_html->SetPage(text);
|
||||
main_sizer->Add(m_opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15);
|
||||
}
|
||||
|
|
@ -198,7 +202,11 @@ void SysInfoDialog::on_dpi_changed(const wxRect &suggested_rect)
|
|||
void SysInfoDialog::onCopyToClipboard(wxEvent &)
|
||||
{
|
||||
wxTheClipboard->Open();
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const auto text = get_main_info(false) + "\n" + wxGetApp().get_gl_info(false, true);
|
||||
#else
|
||||
const auto text = get_main_info(false)+"\n"+_3DScene::get_gl_info(false, true);
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
wxTheClipboard->SetData(new wxTextDataObject(text));
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,7 +230,13 @@ void Tab::create_preset_tab()
|
|||
//! but the OSX version derived from wxOwnerDrawnCombo, instead of:
|
||||
//! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data());
|
||||
//! we doing next:
|
||||
int selected_item = m_presets_choice->GetSelection();
|
||||
// int selected_item = m_presets_choice->GetSelection();
|
||||
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/3889
|
||||
// Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender")
|
||||
// m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive.
|
||||
// So, use GetSelection() from event parameter
|
||||
int selected_item = e.GetSelection();
|
||||
if (m_selected_preset_item == size_t(selected_item) && !m_presets->current_is_dirty())
|
||||
return;
|
||||
if (selected_item >= 0) {
|
||||
|
|
@ -360,9 +366,10 @@ void Tab::update_labels_colour()
|
|||
color = &m_modified_label_clr;
|
||||
}
|
||||
if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") {
|
||||
if (m_colored_Label != nullptr) {
|
||||
m_colored_Label->SetForegroundColour(*color);
|
||||
m_colored_Label->Refresh(true);
|
||||
wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first);
|
||||
if (label) {
|
||||
label->SetForegroundColour(*color);
|
||||
label->Refresh(true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -450,9 +457,10 @@ void Tab::update_changed_ui()
|
|||
tt = &m_tt_white_bullet;
|
||||
}
|
||||
if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") {
|
||||
if (m_colored_Label != nullptr) {
|
||||
m_colored_Label->SetForegroundColour(*color);
|
||||
m_colored_Label->Refresh(true);
|
||||
wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first);
|
||||
if (label) {
|
||||
label->SetForegroundColour(*color);
|
||||
label->Refresh(true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -669,7 +677,8 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
|
|||
|
||||
}
|
||||
if (group->title == _("Profile dependencies")) {
|
||||
if (m_type != Slic3r::Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) {
|
||||
// "compatible_printers" option doesn't exists in Printer Settimgs Tab
|
||||
if (m_type != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) {
|
||||
to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers");
|
||||
load_key_value("compatible_printers", true/*some value*/, true);
|
||||
|
||||
|
|
@ -677,7 +686,8 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
|
|||
m_compatible_printers.checkbox->SetValue(is_empty);
|
||||
is_empty ? m_compatible_printers.btn->Disable() : m_compatible_printers.btn->Enable();
|
||||
}
|
||||
if ((m_type == Slic3r::Preset::TYPE_PRINT || m_type == Slic3r::Preset::TYPE_SLA_PRINT) && (m_options_list["compatible_prints"] & os) == 0) {
|
||||
// "compatible_prints" option exists only in Filament Settimgs and Materials Tabs
|
||||
if ((m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) && (m_options_list["compatible_prints"] & os) == 0) {
|
||||
to_sys ? group->back_to_sys_value("compatible_prints") : group->back_to_initial_value("compatible_prints");
|
||||
load_key_value("compatible_prints", true/*some value*/, true);
|
||||
|
||||
|
|
@ -755,10 +765,17 @@ void Tab::update_visibility()
|
|||
{
|
||||
Freeze(); // There is needed Freeze/Thaw to avoid a flashing after Show/Layout
|
||||
|
||||
// m_detach_preset_btn will be shown always after call page->update_visibility()
|
||||
// So let save a "show state" of m_detach_preset_btn before update_visibility
|
||||
bool was_shown = m_detach_preset_btn->IsShown();
|
||||
|
||||
for (auto page : m_pages)
|
||||
page->update_visibility(m_mode);
|
||||
update_page_tree_visibility();
|
||||
|
||||
// update visibility for detach_preset_btn
|
||||
m_detach_preset_btn->Show(was_shown);
|
||||
|
||||
Layout();
|
||||
Thaw();
|
||||
}
|
||||
|
|
@ -943,6 +960,52 @@ void Tab::on_presets_changed()
|
|||
m_dependent_tabs.clear();
|
||||
}
|
||||
|
||||
void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup)
|
||||
{
|
||||
auto description_line = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_parent_preset_description_line);
|
||||
};
|
||||
|
||||
auto detach_preset_btn = [this](wxWindow* parent) {
|
||||
add_scaled_button(parent, &m_detach_preset_btn, "lock_open_sys", _(L("Detach from system preset")), wxBU_LEFT | wxBU_EXACTFIT);
|
||||
ScalableButton* btn = m_detach_preset_btn;
|
||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent&)
|
||||
{
|
||||
bool system = m_presets->get_edited_preset().is_system;
|
||||
bool dirty = m_presets->get_edited_preset().is_dirty;
|
||||
wxString msg_text = system ?
|
||||
_(L("A copy of the current system preset will be created, which will be detached from the system preset.")) :
|
||||
_(L("The current custom preset will be detached from the parent system preset."));
|
||||
if (dirty) {
|
||||
msg_text += "\n\n";
|
||||
msg_text += _(L("Modifications to the current profile will be saved."));
|
||||
}
|
||||
msg_text += "\n\n";
|
||||
msg_text += _(L("This action is not revertable.\nDo you want to proceed?"));
|
||||
|
||||
wxMessageDialog dialog(parent, msg_text, _(L("Detach preset")), wxICON_WARNING | wxYES_NO | wxCANCEL);
|
||||
if (dialog.ShowModal() == wxID_YES)
|
||||
save_preset(m_presets->get_edited_preset().is_system ? std::string() : m_presets->get_edited_preset().name, true);
|
||||
});
|
||||
|
||||
btn->Hide();
|
||||
|
||||
return sizer;
|
||||
};
|
||||
|
||||
Line line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
|
||||
line.append_widget(description_line);
|
||||
line.append_widget(detach_preset_btn);
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
|
||||
void Tab::update_preset_description_line()
|
||||
{
|
||||
const Preset* parent = m_presets->get_selected_preset_parent();
|
||||
|
|
@ -957,8 +1020,7 @@ void Tab::update_preset_description_line()
|
|||
} else if (parent == nullptr) {
|
||||
description_line = _(L("Current preset is inherited from the default preset."));
|
||||
} else {
|
||||
description_line = from_u8((boost::format(
|
||||
_utf8(L("Current preset is inherited from:\n\t%s"))) % parent->name).str());
|
||||
description_line = _(L("Current preset is inherited from")) + ":\n\t" + parent->name;
|
||||
}
|
||||
|
||||
if (preset.is_default || preset.is_system)
|
||||
|
|
@ -1009,14 +1071,18 @@ void Tab::update_preset_description_line()
|
|||
default: break;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!preset.alias.empty())
|
||||
{
|
||||
description_line += "\n\n\t" + _(L("full profile name")) + ": \n\t\t" + parent->name;
|
||||
description_line += "\n\t" + _(L("symbolic profile name")) + ": \n\t\t" + parent->alias;
|
||||
description_line += "\n\n\t" + _(L("full profile name")) + ": \n\t\t" + preset.name;
|
||||
description_line += "\n\t" + _(L("symbolic profile name")) + ": \n\t\t" + preset.alias;
|
||||
}
|
||||
}
|
||||
|
||||
m_parent_preset_description_line->SetText(description_line, false);
|
||||
|
||||
if (m_detach_preset_btn)
|
||||
m_detach_preset_btn->Show(parent && parent->is_system && !preset.is_default);
|
||||
Layout();
|
||||
}
|
||||
|
||||
void Tab::update_frequently_changed_parameters()
|
||||
|
|
@ -1113,6 +1179,7 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("skirts");
|
||||
optgroup->append_single_option_line("skirt_distance");
|
||||
optgroup->append_single_option_line("skirt_height");
|
||||
optgroup->append_single_option_line("draft_shield");
|
||||
optgroup->append_single_option_line("min_skirt_length");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Brim")));
|
||||
|
|
@ -1258,21 +1325,16 @@ void TabPrint::build()
|
|||
|
||||
page = add_options_page(_(L("Dependencies")), "wrench.png");
|
||||
optgroup = page->new_optgroup(_(L("Profile dependencies")));
|
||||
line = optgroup->create_single_option_line("compatible_printers");
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
|
||||
create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
|
||||
return compatible_widget_create(parent, m_compatible_printers);
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
});
|
||||
|
||||
option = optgroup->get_option("compatible_printers_condition");
|
||||
option.opt.full_width = true;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_parent_preset_description_line);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
build_preset_description_line(optgroup.get());
|
||||
}
|
||||
|
||||
// Reload current config (aka presets->edited_preset->config) into the UI fields.
|
||||
|
|
@ -1304,7 +1366,10 @@ void TabPrint::update()
|
|||
if (m_update_cnt==0) {
|
||||
m_config_manipulation.toggle_print_fff_options(m_config);
|
||||
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
// update() could be called during undo/redo execution
|
||||
// Update of objectList can cause a crash in this case (because m_objects doesn't match ObjectList)
|
||||
if (!wxGetApp().plater()->inside_snapshot_capture())
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
|
||||
wxGetApp().mainframe->on_config_changed(m_config);
|
||||
}
|
||||
|
|
@ -1548,31 +1613,23 @@ void TabFilament::build()
|
|||
|
||||
page = add_options_page(_(L("Dependencies")), "wrench.png");
|
||||
optgroup = page->new_optgroup(_(L("Profile dependencies")));
|
||||
|
||||
line = optgroup->create_single_option_line("compatible_printers");
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
|
||||
return compatible_widget_create(parent, m_compatible_printers);
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
});
|
||||
|
||||
option = optgroup->get_option("compatible_printers_condition");
|
||||
option.opt.full_width = true;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
line = optgroup->create_single_option_line("compatible_prints");
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
create_line_with_widget(optgroup.get(), "compatible_prints", [this](wxWindow* parent) {
|
||||
return compatible_widget_create(parent, m_compatible_prints);
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
});
|
||||
|
||||
option = optgroup->get_option("compatible_prints_condition");
|
||||
option.opt.full_width = true;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_parent_preset_description_line);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
build_preset_description_line(optgroup.get());
|
||||
}
|
||||
|
||||
// Reload current config (aka presets->edited_preset->config) into the UI fields.
|
||||
|
|
@ -1710,7 +1767,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
|||
option.opt.width = Field::def_width_wider();
|
||||
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()) {
|
||||
option = optgroup->get_option("printhost_cafile");
|
||||
|
|
@ -1752,15 +1809,19 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
|||
Line line { "", "" };
|
||||
line.full_width = 1;
|
||||
|
||||
line.widget = [this, ca_file_hint] (wxWindow* parent) {
|
||||
auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%")
|
||||
% (boost::format(_utf8(L("HTTPS CA File:\n\
|
||||
line.widget = [ca_file_hint] (wxWindow* parent) {
|
||||
std::string info = _utf8(L("HTTPS CA File")) + ":\n\t" +
|
||||
(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\
|
||||
\tTo use a custom CA file, please import your CA file into Certificate Store / Keychain."))) % SLIC3R_APP_NAME).str()
|
||||
% 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);
|
||||
sizer->Add(txt);
|
||||
sizer->Add(txt, 1, wxEXPAND);
|
||||
return sizer;
|
||||
};
|
||||
|
||||
|
|
@ -1796,38 +1857,10 @@ void TabPrinter::build_fff()
|
|||
auto page = add_options_page(_(L("General")), "printer");
|
||||
auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
|
||||
|
||||
Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" };
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
ScalableButton* btn;
|
||||
add_scaled_button(parent, &btn, "printer_white", " " + _(L("Set")) + " " + dots, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
btn->SetFont(wxGetApp().normal_font());
|
||||
create_line_with_widget(optgroup.get(), "bed_shape", [this](wxWindow* parent) {
|
||||
return create_bed_shape_widget(parent);
|
||||
});
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_texture"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_model"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
const std::vector<Vec2d>& shape = dlg.get_shape();
|
||||
const std::string& custom_texture = dlg.get_custom_texture();
|
||||
const std::string& custom_model = dlg.get_custom_model();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
load_key_value("bed_custom_texture", custom_texture);
|
||||
load_key_value("bed_custom_model", custom_model);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
optgroup->append_single_option_line("max_print_height");
|
||||
optgroup->append_single_option_line("z_offset");
|
||||
|
||||
|
|
@ -2018,12 +2051,8 @@ void TabPrinter::build_fff()
|
|||
|
||||
page = add_options_page(_(L("Dependencies")), "wrench.png");
|
||||
optgroup = page->new_optgroup(_(L("Profile dependencies")));
|
||||
line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_parent_preset_description_line);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
build_preset_description_line(optgroup.get());
|
||||
|
||||
build_unregular_pages();
|
||||
|
||||
|
|
@ -2040,39 +2069,9 @@ void TabPrinter::build_sla()
|
|||
auto page = add_options_page(_(L("General")), "printer");
|
||||
auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
|
||||
|
||||
Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" };
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
ScalableButton* btn;
|
||||
add_scaled_button(parent, &btn, "printer_white", " " + _(L("Set")) + " " + dots, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
btn->SetFont(wxGetApp().normal_font());
|
||||
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_texture"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_model"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
const std::vector<Vec2d>& shape = dlg.get_shape();
|
||||
const std::string& custom_texture = dlg.get_custom_texture();
|
||||
const std::string& custom_model = dlg.get_custom_model();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
load_key_value("bed_custom_texture", custom_texture);
|
||||
load_key_value("bed_custom_model", custom_model);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
create_line_with_widget(optgroup.get(), "bed_shape", [this](wxWindow* parent) {
|
||||
return create_bed_shape_widget(parent);
|
||||
});
|
||||
optgroup->append_single_option_line("max_print_height");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Display")));
|
||||
|
|
@ -2080,7 +2079,7 @@ void TabPrinter::build_sla()
|
|||
optgroup->append_single_option_line("display_height");
|
||||
|
||||
auto option = optgroup->get_option("display_pixels_x");
|
||||
line = { _(option.opt.full_label), "" };
|
||||
Line line = { _(option.opt.full_label), "" };
|
||||
line.append_option(option);
|
||||
line.append_option(optgroup->get_option("display_pixels_y"));
|
||||
optgroup->append_line(line);
|
||||
|
|
@ -2134,12 +2133,8 @@ void TabPrinter::build_sla()
|
|||
|
||||
page = add_options_page(_(L("Dependencies")), "wrench.png");
|
||||
optgroup = page->new_optgroup(_(L("Profile dependencies")));
|
||||
line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_parent_preset_description_line);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
build_preset_description_line(optgroup.get());
|
||||
}
|
||||
|
||||
void TabPrinter::update_serial_ports()
|
||||
|
|
@ -2602,6 +2597,15 @@ void TabPrinter::update_fff()
|
|||
void TabPrinter::update_sla()
|
||||
{ ; }
|
||||
|
||||
void Tab::update_ui_items_related_on_parent_preset(const Preset* selected_preset_parent)
|
||||
{
|
||||
m_is_default_preset = selected_preset_parent != nullptr && selected_preset_parent->is_default;
|
||||
|
||||
m_bmp_non_system = selected_preset_parent ? &m_bmp_value_unlock : &m_bmp_white_bullet;
|
||||
m_ttg_non_system = selected_preset_parent ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
|
||||
m_tt_non_system = selected_preset_parent ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
|
||||
}
|
||||
|
||||
// Initialize the UI from the current preset
|
||||
void Tab::load_current_preset()
|
||||
{
|
||||
|
|
@ -2620,12 +2624,7 @@ void Tab::load_current_preset()
|
|||
// Reload preset pages with the new configuration values.
|
||||
reload_config();
|
||||
|
||||
const Preset* selected_preset_parent = m_presets->get_selected_preset_parent();
|
||||
m_is_default_preset = selected_preset_parent != nullptr && selected_preset_parent->is_default;
|
||||
|
||||
m_bmp_non_system = selected_preset_parent ? &m_bmp_value_unlock : &m_bmp_white_bullet;
|
||||
m_ttg_non_system = selected_preset_parent ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
|
||||
m_tt_non_system = selected_preset_parent ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
|
||||
update_ui_items_related_on_parent_preset(m_presets->get_selected_preset_parent());
|
||||
|
||||
// m_undo_to_sys_btn->Enable(!preset.is_default);
|
||||
|
||||
|
|
@ -2777,7 +2776,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current)
|
|||
bool printer_tab = m_presets->type() == Preset::TYPE_PRINTER;
|
||||
bool canceled = false;
|
||||
bool technology_changed = false;
|
||||
m_dependent_tabs = {};
|
||||
m_dependent_tabs.clear();
|
||||
if (current_dirty && ! may_discard_current_dirty_preset()) {
|
||||
canceled = true;
|
||||
} else if (print_tab) {
|
||||
|
|
@ -2873,10 +2872,10 @@ void Tab::select_preset(std::string preset_name, bool delete_current)
|
|||
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
||||
// The following method should not discard changes of current print or filament presets on change of a printer profile,
|
||||
// if they are compatible with the current printer.
|
||||
auto update_compatible_type = [](bool technology_changed, bool on_page, bool show_incompatible_presets) {
|
||||
return technology_changed ? PresetSelectCompatibleType::Always :
|
||||
on_page ? PresetSelectCompatibleType::Never :
|
||||
(show_incompatible_presets ? PresetSelectCompatibleType::OnlyIfWasCompatible : PresetSelectCompatibleType::Always);
|
||||
auto update_compatible_type = [delete_current](bool technology_changed, bool on_page, bool show_incompatible_presets) {
|
||||
return (delete_current || technology_changed) ? PresetSelectCompatibleType::Always :
|
||||
on_page ? PresetSelectCompatibleType::Never :
|
||||
show_incompatible_presets ? PresetSelectCompatibleType::OnlyIfWasCompatible : PresetSelectCompatibleType::Always;
|
||||
};
|
||||
if (current_dirty || delete_current || print_tab || printer_tab)
|
||||
m_preset_bundle->update_compatible(
|
||||
|
|
@ -3031,18 +3030,20 @@ void Tab::OnKeyDown(wxKeyEvent& event)
|
|||
// and activates the new preset.
|
||||
// Wizard calls save_preset with a name "My Settings", otherwise no name is provided and this method
|
||||
// opens a Slic3r::GUI::SavePresetWindow dialog.
|
||||
void Tab::save_preset(std::string name /*= ""*/)
|
||||
void Tab::save_preset(std::string name /*= ""*/, bool detach)
|
||||
{
|
||||
// since buttons(and choices too) don't get focus on Mac, we set focus manually
|
||||
// to the treectrl so that the EVT_* events are fired for the input field having
|
||||
// focus currently.is there anything better than this ?
|
||||
//! m_treectrl->OnSetFocus();
|
||||
|
||||
std::string suffix = detach ? _utf8(L("Detached")) : _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName");
|
||||
|
||||
if (name.empty()) {
|
||||
const Preset &preset = m_presets->get_selected_preset();
|
||||
auto default_name = preset.is_default ? "Untitled" :
|
||||
// preset.is_system ? (boost::format(_utf8(L("%1% - Copy"))) % preset.name).str() :
|
||||
preset.is_system ? (boost::format(_CTX_utf8(L_CONTEXT("%1% - Copy", "PresetName"), "PresetName")) % preset.name).str() :
|
||||
// preset.is_system ? (boost::format(_CTX_utf8(L_CONTEXT("%1% - Copy", "PresetName"), "PresetName")) % preset.name).str() :
|
||||
preset.is_system ? (boost::format(("%1% - %2%")) % preset.name % suffix).str() :
|
||||
preset.name;
|
||||
|
||||
bool have_extention = boost::iends_with(default_name, ".ini");
|
||||
|
|
@ -3092,8 +3093,9 @@ void Tab::save_preset(std::string name /*= ""*/)
|
|||
}
|
||||
|
||||
// Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini
|
||||
m_presets->save_current_preset(name);
|
||||
m_presets->save_current_preset(name, detach);
|
||||
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
||||
// If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible.
|
||||
m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never);
|
||||
// Add the new item into the UI component, remove dirty flags and activate the saved item.
|
||||
update_tab_ui();
|
||||
|
|
@ -3104,6 +3106,11 @@ void Tab::save_preset(std::string name /*= ""*/)
|
|||
|
||||
if (m_type == Preset::TYPE_PRINTER)
|
||||
static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count;
|
||||
|
||||
// Parent preset is "default" after detaching, so we should to update UI values, related on parent preset
|
||||
if (detach)
|
||||
update_ui_items_related_on_parent_preset(m_presets->get_selected_preset_parent());
|
||||
|
||||
update_changed_ui();
|
||||
|
||||
/* If filament preset is saved for multi-material printer preset,
|
||||
|
|
@ -3111,6 +3118,30 @@ void Tab::save_preset(std::string name /*= ""*/)
|
|||
* but in full_config a filament_colors option aren't.*/
|
||||
if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1)
|
||||
wxGetApp().plater()->force_filament_colors_update();
|
||||
|
||||
{
|
||||
// Profile compatiblity is updated first when the profile is saved.
|
||||
// Update profile selection combo boxes at the depending tabs to reflect modifications in profile compatibility.
|
||||
std::vector<Preset::Type> dependent;
|
||||
switch (m_type) {
|
||||
case Preset::TYPE_PRINT:
|
||||
dependent = { Preset::TYPE_FILAMENT };
|
||||
break;
|
||||
case Preset::TYPE_SLA_PRINT:
|
||||
dependent = { Preset::TYPE_SLA_MATERIAL };
|
||||
break;
|
||||
case Preset::TYPE_PRINTER:
|
||||
if (static_cast<const TabPrinter*>(this)->m_printer_technology == ptFFF)
|
||||
dependent = { Preset::TYPE_PRINT, Preset::TYPE_FILAMENT };
|
||||
else
|
||||
dependent = { Preset::TYPE_SLA_PRINT, Preset::TYPE_SLA_MATERIAL };
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (Preset::Type preset_type : dependent)
|
||||
wxGetApp().get_tab(preset_type)->update_tab_ui();
|
||||
}
|
||||
}
|
||||
|
||||
// Called for a currently selected preset.
|
||||
|
|
@ -3168,6 +3199,15 @@ void Tab::update_ui_from_settings()
|
|||
}
|
||||
}
|
||||
|
||||
void Tab::create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget)
|
||||
{
|
||||
Line line = optgroup->create_single_option_line(opt_key);
|
||||
line.widget = widget;
|
||||
|
||||
m_colored_Labels[opt_key] = nullptr;
|
||||
optgroup->append_line(line, &m_colored_Labels[opt_key]);
|
||||
}
|
||||
|
||||
// Return a callback to create a Tab widget to mark the preferences as compatible / incompatible to the current printer.
|
||||
wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &deps)
|
||||
{
|
||||
|
|
@ -3239,6 +3279,39 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
|
|||
return sizer;
|
||||
}
|
||||
|
||||
// Return a callback to create a TabPrinter widget to edit bed shape
|
||||
wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent)
|
||||
{
|
||||
ScalableButton* btn;
|
||||
add_scaled_button(parent, &btn, "printer_white", " " + _(L("Set")) + " " + dots, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
btn->SetFont(wxGetApp().normal_font());
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_texture"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_model"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
const std::vector<Vec2d>& shape = dlg.get_shape();
|
||||
const std::string& custom_texture = dlg.get_custom_texture();
|
||||
const std::string& custom_model = dlg.get_custom_model();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
load_key_value("bed_custom_texture", custom_texture);
|
||||
load_key_value("bed_custom_model", custom_model);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
}
|
||||
|
||||
void Tab::compatible_widget_reload(PresetDependencies &deps)
|
||||
{
|
||||
bool has_any = ! m_config->option<ConfigOptionStrings>(deps.key_list)->values.empty();
|
||||
|
|
@ -3262,8 +3335,8 @@ void Tab::fill_icon_descriptions()
|
|||
|
||||
m_icon_descriptions.emplace_back(&m_bmp_white_bullet, L("WHITE BULLET"),
|
||||
// TRN Description for "WHITE BULLET"
|
||||
L("for the left button: \tindicates a non-system (or non-default) preset,\n"
|
||||
"for the right button: \tindicates that the settings hasn't been modified."));
|
||||
L("for the left button: indicates a non-system (or non-default) preset,\n"
|
||||
"for the right button: indicates that the settings hasn't been modified."));
|
||||
|
||||
m_icon_descriptions.emplace_back(&m_bmp_value_revert, L("BACK ARROW"),
|
||||
// TRN Description for "BACK ARROW"
|
||||
|
|
@ -3414,7 +3487,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)
|
||||
{
|
||||
// TRN Preset
|
||||
auto text = new wxStaticText(this, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % title).str()),
|
||||
auto text = new wxStaticText(this, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(title)).str()),
|
||||
wxDefaultPosition, wxDefaultSize);
|
||||
m_combo = new wxComboBox(this, wxID_ANY, from_u8(default_name),
|
||||
wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER);
|
||||
|
|
@ -3542,30 +3615,24 @@ void TabSLAMaterial::build()
|
|||
|
||||
page = add_options_page(_(L("Dependencies")), "wrench.png");
|
||||
optgroup = page->new_optgroup(_(L("Profile dependencies")));
|
||||
Line line = optgroup->create_single_option_line("compatible_printers");
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
|
||||
create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
|
||||
return compatible_widget_create(parent, m_compatible_printers);
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
});
|
||||
|
||||
option = optgroup->get_option("compatible_printers_condition");
|
||||
option.opt.full_width = true;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
line = optgroup->create_single_option_line("compatible_prints");
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
create_line_with_widget(optgroup.get(), "compatible_prints", [this](wxWindow* parent) {
|
||||
return compatible_widget_create(parent, m_compatible_prints);
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
});
|
||||
|
||||
option = optgroup->get_option("compatible_prints_condition");
|
||||
option.opt.full_width = true;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_parent_preset_description_line);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
build_preset_description_line(optgroup.get());
|
||||
}
|
||||
|
||||
// Reload current config (aka presets->edited_preset->config) into the UI fields.
|
||||
|
|
@ -3672,22 +3739,16 @@ void TabSLAPrint::build()
|
|||
|
||||
page = add_options_page(_(L("Dependencies")), "wrench");
|
||||
optgroup = page->new_optgroup(_(L("Profile dependencies")));
|
||||
Line line = optgroup->create_single_option_line("compatible_printers");//Line { _(L("Compatible printers")), "" };
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
|
||||
create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
|
||||
return compatible_widget_create(parent, m_compatible_printers);
|
||||
};
|
||||
optgroup->append_line(line, &m_colored_Label);
|
||||
});
|
||||
|
||||
option = optgroup->get_option("compatible_printers_condition");
|
||||
option.opt.full_width = true;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_parent_preset_description_line);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
build_preset_description_line(optgroup.get());
|
||||
}
|
||||
|
||||
// Reload current config (aka presets->edited_preset->config) into the UI fields.
|
||||
|
|
@ -3710,7 +3771,10 @@ void TabSLAPrint::update()
|
|||
if (m_update_cnt == 0) {
|
||||
m_config_manipulation.toggle_print_sla_options(m_config);
|
||||
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
// update() could be called during undo/redo execution
|
||||
// Update of objectList can cause a crash in this case (because m_objects doesn't match ObjectList)
|
||||
if (!wxGetApp().plater()->inside_snapshot_capture())
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
|
||||
wxGetApp().mainframe->on_config_changed(m_config);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ protected:
|
|||
bool m_disable_tree_sel_changed_event;
|
||||
bool m_show_incompatible_presets;
|
||||
|
||||
std::vector<Preset::Type> m_dependent_tabs = {};
|
||||
std::vector<Preset::Type> m_dependent_tabs;
|
||||
enum OptStatus { osSystemValue = 1, osInitValue = 2 };
|
||||
std::map<std::string, int> m_options_list;
|
||||
int m_opt_status_value = 0;
|
||||
|
|
@ -227,7 +227,12 @@ public:
|
|||
PresetCollection* m_presets;
|
||||
DynamicPrintConfig* m_config;
|
||||
ogStaticText* m_parent_preset_description_line;
|
||||
wxStaticText* m_colored_Label = nullptr;
|
||||
ScalableButton* m_detach_preset_btn = nullptr;
|
||||
|
||||
// map of option name -> wxStaticText (colored label, associated with option)
|
||||
// Used for options which don't have corresponded field
|
||||
std::map<std::string, wxStaticText*> m_colored_Labels;
|
||||
|
||||
// Counter for the updating (because of an update() function can have a recursive behavior):
|
||||
// 1. increase value from the very beginning of an update() function
|
||||
// 2. decrease value at the end of an update() function
|
||||
|
|
@ -253,6 +258,7 @@ public:
|
|||
const wxString& label = wxEmptyString,
|
||||
long style = wxBU_EXACTFIT | wxNO_BORDER);
|
||||
void add_scaled_bitmap(wxWindow* parent, ScalableBitmap& btn, const std::string& icon_name);
|
||||
void update_ui_items_related_on_parent_preset(const Preset* selected_preset_parent);
|
||||
void load_current_preset();
|
||||
void rebuild_page_tree();
|
||||
void update_page_tree_visibility();
|
||||
|
|
@ -264,7 +270,7 @@ public:
|
|||
void OnTreeSelChange(wxTreeEvent& event);
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
|
||||
void save_preset(std::string name = "");
|
||||
void save_preset(std::string name = std::string(), bool detach = false);
|
||||
void delete_preset();
|
||||
void toggle_show_hide_incompatible();
|
||||
void update_show_hide_incompatible_button();
|
||||
|
|
@ -306,11 +312,13 @@ public:
|
|||
void update_wiping_button_visibility();
|
||||
|
||||
protected:
|
||||
void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget);
|
||||
wxSizer* compatible_widget_create(wxWindow* parent, PresetDependencies &deps);
|
||||
void compatible_widget_reload(PresetDependencies &deps);
|
||||
void load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value = false);
|
||||
|
||||
void on_presets_changed();
|
||||
void build_preset_description_line(ConfigOptionsGroup* optgroup);
|
||||
void update_preset_description_line();
|
||||
void update_frequently_changed_parameters();
|
||||
void fill_icon_descriptions();
|
||||
|
|
@ -406,6 +414,8 @@ public:
|
|||
void init_options_list() override;
|
||||
void msw_rescale() override;
|
||||
bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; }
|
||||
|
||||
wxSizer* create_bed_shape_widget(wxWindow* parent);
|
||||
};
|
||||
|
||||
class TabSLAMaterial : public Tab
|
||||
|
|
|
|||
65
src/slic3r/GUI/format.hpp
Normal file
65
src/slic3r/GUI/format.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef slic3r_GUI_format_hpp_
|
||||
#define slic3r_GUI_format_hpp_
|
||||
|
||||
// Functional wrapper around boost::format.
|
||||
// One day we may replace this wrapper with C++20 format
|
||||
// https://en.cppreference.com/w/cpp/utility/format/format
|
||||
// though C++20 format uses a different template pattern for position independent parameters.
|
||||
// This wrapper also manages implicit conversion from wxString to UTF8 and format_wxstr() variants are provided to format into wxString.
|
||||
|
||||
#include <libslic3r/format.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
// Format input mixing UTF8 encoded strings (const char*, std::string) and wxStrings, return a wxString.
|
||||
template<typename... TArgs>
|
||||
inline wxString format_wxstr(const char* fmt, TArgs&&... args) {
|
||||
boost::format message(fmt);
|
||||
return wxString::FromUTF8(Slic3r::internal::format::format_recursive(message, std::forward<TArgs>(args)...).c_str());
|
||||
}
|
||||
template<typename... TArgs>
|
||||
inline wxString format_wxstr(const std::string& fmt, TArgs&&... args) {
|
||||
boost::format message(fmt);
|
||||
return wxString::FromUTF8(Slic3r::internal::format::format_recursive(message, std::forward<TArgs>(args)...).c_str());
|
||||
}
|
||||
template<typename... TArgs>
|
||||
inline wxString format_wxstr(const wxString& fmt, TArgs&&... args) {
|
||||
return format_wxstr(fmt.ToUTF8().data(), std::forward<TArgs>(args)...);
|
||||
}
|
||||
template<typename... TArgs>
|
||||
inline std::string format(const wxString& fmt, TArgs&&... args) {
|
||||
return format(fmt.ToUTF8().data(), std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
||||
namespace internal {
|
||||
namespace format {
|
||||
// Wrapper around wxScopedCharBuffer to indicate that the content is UTF8 formatted.
|
||||
struct utf8_buffer {
|
||||
// wxScopedCharBuffer is reference counted, therefore copying by value is cheap.
|
||||
wxScopedCharBuffer data;
|
||||
};
|
||||
// Accept wxString and convert it to UTF8 to be processed by Slic3r::format().
|
||||
inline const utf8_buffer cook(const wxString &arg) {
|
||||
return utf8_buffer { arg.ToUTF8() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
namespace boost {
|
||||
namespace io {
|
||||
namespace detail {
|
||||
// Adaptor for boost::format to accept wxString converted to UTF8.
|
||||
inline std::ostream& operator<<(std::ostream& os, const Slic3r::internal::format::utf8_buffer& str) {
|
||||
os << str.data.data();
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* slic3r_GUI_format_hpp_ */
|
||||
|
|
@ -604,7 +604,7 @@ void apply_extruder_selector(wxBitmapComboBox** ctrl,
|
|||
}
|
||||
|
||||
(*ctrl)->Append(use_full_item_name
|
||||
? wxString::FromUTF8((boost::format("%1% %2%") % str % i).str())
|
||||
? Slic3r::GUI::from_u8((boost::format("%1% %2%") % str % i).str())
|
||||
: wxString::Format("%d", i), *bmp);
|
||||
++i;
|
||||
}
|
||||
|
|
@ -703,8 +703,8 @@ ModeButton::ModeButton( wxWindow* parent,
|
|||
void ModeButton::Init(const wxString &mode)
|
||||
{
|
||||
std::string mode_str = std::string(mode.ToUTF8());
|
||||
m_tt_focused = wxString::FromUTF8((boost::format(_utf8(L("Switch to the %s mode"))) % mode_str).str());
|
||||
m_tt_selected = wxString::FromUTF8((boost::format(_utf8(L("Current mode is %s"))) % mode_str).str());
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ wxString FlashAir::get_test_ok_msg () const
|
|||
|
||||
wxString FlashAir::get_test_failed_msg (wxString &msg) const
|
||||
{
|
||||
return wxString::FromUTF8((boost::format("%s: %s")
|
||||
return GUI::from_u8((boost::format("%s: %s\n%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());
|
||||
|
|
|
|||
|
|
@ -7,10 +7,17 @@
|
|||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#ifdef OPENSSL_CERT_OVERRIDE
|
||||
#include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
|
|
@ -22,14 +29,56 @@ namespace Slic3r {
|
|||
|
||||
// Private
|
||||
|
||||
class CurlGlobalInit
|
||||
struct CurlGlobalInit
|
||||
{
|
||||
static const CurlGlobalInit instance;
|
||||
static std::unique_ptr<CurlGlobalInit> instance;
|
||||
|
||||
CurlGlobalInit() { ::curl_global_init(CURL_GLOBAL_DEFAULT); }
|
||||
CurlGlobalInit()
|
||||
{
|
||||
#ifdef OPENSSL_CERT_OVERRIDE // defined if SLIC3R_STATIC=ON
|
||||
|
||||
// Look for a set of distro specific directories. Don't change the
|
||||
// order: https://bugzilla.redhat.com/show_bug.cgi?id=1053882
|
||||
static const char * CA_BUNDLES[] = {
|
||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||
"/usr/share/ssl/certs/ca-bundle.crt",
|
||||
"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD
|
||||
"/etc/ssl/cert.pem",
|
||||
"/etc/ssl/ca-bundle.pem" // OpenSUSE Tumbleweed
|
||||
};
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
// Env var name for the OpenSSL CA bundle (SSL_CERT_FILE nomally)
|
||||
const char *const SSL_CA_FILE = X509_get_default_cert_file_env();
|
||||
const char * ssl_cafile = ::getenv(SSL_CA_FILE);
|
||||
|
||||
if (!ssl_cafile)
|
||||
ssl_cafile = X509_get_default_cert_file();
|
||||
|
||||
int replace = true;
|
||||
|
||||
if (!ssl_cafile || !fs::exists(fs::path(ssl_cafile)))
|
||||
for (const char * bundle : CA_BUNDLES) {
|
||||
if (fs::exists(fs::path(bundle))) {
|
||||
::setenv(SSL_CA_FILE, bundle, replace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Detected OpenSSL root CA store: " << ::getenv(SSL_CA_FILE);
|
||||
|
||||
#endif
|
||||
|
||||
::curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
}
|
||||
|
||||
~CurlGlobalInit() { ::curl_global_cleanup(); }
|
||||
};
|
||||
|
||||
std::unique_ptr<CurlGlobalInit> CurlGlobalInit::instance;
|
||||
|
||||
struct Http::priv
|
||||
{
|
||||
enum {
|
||||
|
|
@ -83,6 +132,9 @@ Http::priv::priv(const std::string &url)
|
|||
, limit(0)
|
||||
, cancel(false)
|
||||
{
|
||||
if (!CurlGlobalInit::instance)
|
||||
CurlGlobalInit::instance = std::make_unique<CurlGlobalInit>();
|
||||
|
||||
if (curl == nullptr) {
|
||||
throw std::runtime_error(std::string("Could not construct Curl object"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
#include "slic3r/GUI/UpdateDialogs.hpp"
|
||||
#include "slic3r/GUI/ConfigWizard.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "slic3r/Config/Version.hpp"
|
||||
#include "slic3r/Config/Snapshot.hpp"
|
||||
|
|
@ -54,7 +56,7 @@ void copy_file_fix(const fs::path &source, const fs::path &target)
|
|||
{
|
||||
static const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; // aka 644
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("PresetUpdater: Copying %1% -> %2%") % source % target;
|
||||
BOOST_LOG_TRIVIAL(debug) << format("PresetUpdater: Copying %1% -> %2%", source, target);
|
||||
|
||||
// Make sure the file has correct permission both before and after we copy over it
|
||||
if (fs::exists(target)) {
|
||||
|
|
@ -190,12 +192,12 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe
|
|||
{
|
||||
bool res = false;
|
||||
fs::path tmp_path = target_path;
|
||||
tmp_path += (boost::format(".%1%%2%") % get_current_pid() % TMP_EXTENSION).str();
|
||||
tmp_path += format(".%1%%2%", get_current_pid(), TMP_EXTENSION);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Get: `%1%`\n\t-> `%2%`\n\tvia tmp path `%3%`")
|
||||
% url
|
||||
% target_path.string()
|
||||
% tmp_path.string();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Get: `%1%`\n\t-> `%2%`\n\tvia tmp path `%3%`",
|
||||
url,
|
||||
target_path.string(),
|
||||
tmp_path.string());
|
||||
|
||||
Http::get(url)
|
||||
.on_progress([this](Http::Progress, bool &cancel) {
|
||||
|
|
@ -203,10 +205,10 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe
|
|||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||
(void)body;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%")
|
||||
% url
|
||||
% http_status
|
||||
% error;
|
||||
BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%",
|
||||
url,
|
||||
http_status,
|
||||
error);
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned /* http_status */) {
|
||||
fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
|
|
@ -235,7 +237,7 @@ void PresetUpdater::priv::sync_version() const
|
|||
{
|
||||
if (! enabled_version_check) { return; }
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Downloading %1% online version from: `%2%`") % SLIC3R_APP_NAME % version_check_url;
|
||||
BOOST_LOG_TRIVIAL(info) << format("Downloading %1% online version from: `%2%`", SLIC3R_APP_NAME, version_check_url);
|
||||
|
||||
Http::get(version_check_url)
|
||||
.size_limit(SLIC3R_VERSION_BODY_MAX)
|
||||
|
|
@ -244,10 +246,10 @@ void PresetUpdater::priv::sync_version() const
|
|||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||
(void)body;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%")
|
||||
% version_check_url
|
||||
% http_status
|
||||
% error;
|
||||
BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%",
|
||||
version_check_url,
|
||||
http_status,
|
||||
error);
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned /* http_status */) {
|
||||
boost::trim(body);
|
||||
|
|
@ -257,11 +259,11 @@ void PresetUpdater::priv::sync_version() const
|
|||
}
|
||||
|
||||
if (! Semver::parse(body)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("Received invalid contents from `%1%`: Not a correct semver: `%2%`") % SLIC3R_APP_NAME % body;
|
||||
BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, body);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Got %1% online version: `%2%`. Sending to GUI thread...") % SLIC3R_APP_NAME % body;
|
||||
BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, body);
|
||||
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE);
|
||||
evt->SetString(GUI::from_u8(body));
|
||||
|
|
@ -315,11 +317,11 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors)
|
|||
try {
|
||||
new_index.load(idx_path_temp);
|
||||
} catch (const std::exception & /* err */) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Could not load downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name;
|
||||
BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path_temp, vendor.name);
|
||||
continue;
|
||||
}
|
||||
if (new_index.version() < index.version()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.") % idx_path_temp % vendor.name;
|
||||
BOOST_LOG_TRIVIAL(warning) << format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.", idx_path_temp, vendor.name);
|
||||
continue;
|
||||
}
|
||||
Slic3r::rename_file(idx_path_temp, idx_path);
|
||||
|
|
@ -331,22 +333,22 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors)
|
|||
// See if a there's a new version to download
|
||||
const auto recommended_it = index.recommended();
|
||||
if (recommended_it == index.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % vendor.name;
|
||||
BOOST_LOG_TRIVIAL(error) << format("No recommended version for vendor: %1%, invalid index?", vendor.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto recommended = recommended_it->config_version;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Got index for vendor: %1%: current version: %2%, recommended version: %3%")
|
||||
% vendor.name
|
||||
% vendor.config_version.to_string()
|
||||
% recommended.to_string();
|
||||
BOOST_LOG_TRIVIAL(debug) << format("Got index for vendor: %1%: current version: %2%, recommended version: %3%",
|
||||
vendor.name,
|
||||
vendor.config_version.to_string(),
|
||||
recommended.to_string());
|
||||
|
||||
if (vendor.config_version >= recommended) { continue; }
|
||||
|
||||
// Download a fresh bundle
|
||||
BOOST_LOG_TRIVIAL(info) << "Downloading new bundle for vendor: " << vendor.name;
|
||||
const auto bundle_url = (boost::format("%1%/%2%.ini") % vendor.config_update_url % recommended.to_string()).str();
|
||||
const auto bundle_url = format("%1%/%2%.ini", vendor.config_update_url, recommended.to_string());
|
||||
const auto bundle_path = cache_path / (vendor.id + ".ini");
|
||||
if (! get_file(bundle_url, bundle_path)) { continue; }
|
||||
if (cancel) { return; }
|
||||
|
|
@ -394,7 +396,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
auto bundle_path_idx = vendor_path / idx.path().filename();
|
||||
|
||||
if (! fs::exists(bundle_path)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Confing bundle not installed for vendor %1%, skipping: " << idx.vendor();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Confing bundle not installed for vendor %1%, skipping: ", idx.vendor());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -405,7 +407,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
// from the internet, or installed / updated from the installation resources.
|
||||
auto recommended = idx.recommended();
|
||||
if (recommended == idx.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index? Giving up.") % idx.vendor();
|
||||
BOOST_LOG_TRIVIAL(error) << format("No recommended version for vendor: %1%, invalid index? Giving up.", idx.vendor());
|
||||
// XXX: what should be done here?
|
||||
continue;
|
||||
}
|
||||
|
|
@ -413,15 +415,15 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
const auto ver_current = idx.find(vp.config_version);
|
||||
const bool ver_current_found = ver_current != idx.end();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Vendor: %1%, version installed: %2%%3%, version cached: %4%")
|
||||
% vp.name
|
||||
% vp.config_version.to_string()
|
||||
% (ver_current_found ? "" : " (not found in index!)")
|
||||
% recommended->config_version.to_string();
|
||||
BOOST_LOG_TRIVIAL(debug) << format("Vendor: %1%, version installed: %2%%3%, version cached: %4%",
|
||||
vp.name,
|
||||
vp.config_version.to_string(),
|
||||
(ver_current_found ? "" : " (not found in index!)"),
|
||||
recommended->config_version.to_string());
|
||||
|
||||
if (! ver_current_found) {
|
||||
// Any published config shall be always found in the latest config index.
|
||||
auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
|
||||
auto message = format("Preset bundle `%1%` version not found in index: %2%", idx.vendor(), vp.config_version.to_string());
|
||||
BOOST_LOG_TRIVIAL(error) << message;
|
||||
GUI::show_error(nullptr, message);
|
||||
continue;
|
||||
|
|
@ -440,7 +442,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
}
|
||||
|
||||
if (recommended->config_version < vp.config_version) {
|
||||
BOOST_LOG_TRIVIAL(warning) << (boost::format("Recommended config version for the currently running PrusaSlicer is older than the currently installed config for vendor %1%. This should not happen.") % idx.vendor()).str();
|
||||
BOOST_LOG_TRIVIAL(warning) << format("Recommended config version for the currently running PrusaSlicer is older than the currently installed config for vendor %1%. This should not happen.", idx.vendor());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +484,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
found = true;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Failed to load the config bundle `%1%`: %2%") % path_in_cache.string() % ex.what();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Failed to load the config bundle `%1%`: %2%", path_in_cache.string(), ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -496,13 +498,13 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
try {
|
||||
rsrc_vp = VendorProfile::from_ini(path_in_rsrc, false);
|
||||
} catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Cannot load the config bundle at `%1%`: %2%") % path_in_rsrc.string() % ex.what();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Cannot load the config bundle at `%1%`: %2%", path_in_rsrc.string(), ex.what());
|
||||
}
|
||||
if (rsrc_vp.valid()) {
|
||||
try {
|
||||
rsrc_idx.load(path_idx_in_rsrc);
|
||||
} catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Cannot load the config index at `%1%`: %2%") % path_idx_in_rsrc.string() % ex.what();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Cannot load the config index at `%1%`: %2%", path_idx_in_rsrc.string(), ex.what());
|
||||
}
|
||||
recommended = rsrc_idx.recommended();
|
||||
if (recommended != rsrc_idx.end() && recommended->config_version == rsrc_vp.config_version && recommended->config_version > vp.config_version) {
|
||||
|
|
@ -510,8 +512,8 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
bundle_path_idx_to_install = path_idx_in_rsrc;
|
||||
found = true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << (boost::format("The recommended config version for vendor `%1%` in resources does not match the recommended\n"
|
||||
" config version for this version of PrusaSlicer. Corrupted installation?") % idx.vendor()).str();
|
||||
BOOST_LOG_TRIVIAL(warning) << format("The recommended config version for vendor `%1%` in resources does not match the recommended\n"
|
||||
" config version for this version of PrusaSlicer. Corrupted installation?", idx.vendor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -529,11 +531,11 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
/*const auto existing_recommended = existing_idx.recommended(old_slic3r_version);
|
||||
if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
|
||||
// The user has already seen (and presumably rejected) this update
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Downloaded index for `%1%` is the same as installed one, not offering an update.",idx.vendor());
|
||||
continue;
|
||||
}*/
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Cannot load the installed index at `%1%`: %2%") % bundle_path_idx % err.what();
|
||||
BOOST_LOG_TRIVIAL(error) << format("Cannot load the installed index at `%1%`: %2%", bundle_path_idx, err.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -542,10 +544,10 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
{
|
||||
const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version);
|
||||
if (recommended_snap != SnapshotDB::singleton().end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...")
|
||||
% vp.name
|
||||
% recommended->config_version.to_string()
|
||||
% recommended_snap->id;
|
||||
BOOST_LOG_TRIVIAL(info) << format("Bundle update %1% %2% already found in snapshot %3%, skipping...",
|
||||
vp.name,
|
||||
recommended->config_version.to_string(),
|
||||
recommended_snap->id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -555,9 +557,9 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
// offered updates and to not offer the same update again if it was cancelled by the user.
|
||||
copy_file_fix(bundle_path_idx_to_install, bundle_path_idx);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
|
||||
% idx.vendor()
|
||||
% recommended->config_version.to_string();
|
||||
BOOST_LOG_TRIVIAL(warning) << format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources",
|
||||
idx.vendor(),
|
||||
recommended->config_version.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -572,7 +574,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||
SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Deleting %1% incompatible bundles", updates.incompats.size());
|
||||
|
||||
for (auto &incompat : updates.incompats) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << incompat;
|
||||
|
|
@ -587,7 +589,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||
SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Performing %1% updates") % updates.updates.size();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Performing %1% updates", updates.updates.size());
|
||||
|
||||
for (const auto &update : updates.updates) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << update;
|
||||
|
|
@ -597,8 +599,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||
PresetBundle bundle;
|
||||
bundle.load_configbundle(update.source.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% conflicting presets")
|
||||
% (bundle.prints.size() + bundle.filaments.size() + bundle.printers.size());
|
||||
BOOST_LOG_TRIVIAL(info) << format("Deleting %1% conflicting presets", bundle.prints.size() + bundle.filaments.size() + bundle.printers.size());
|
||||
|
||||
auto preset_remover = [](const Preset &preset) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << preset.file;
|
||||
|
|
@ -611,8 +612,8 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||
|
||||
// Also apply the `obsolete_presets` property, removing obsolete ini files
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% obsolete presets")
|
||||
% (bundle.obsolete_presets.prints.size() + bundle.obsolete_presets.filaments.size() + bundle.obsolete_presets.printers.size());
|
||||
BOOST_LOG_TRIVIAL(info) << format("Deleting %1% obsolete presets",
|
||||
bundle.obsolete_presets.prints.size() + bundle.obsolete_presets.filaments.size() + bundle.obsolete_presets.printers.size());
|
||||
|
||||
auto obsolete_remover = [](const char *subdir, const std::string &preset) {
|
||||
auto path = fs::path(Slic3r::data_dir()) / subdir / preset;
|
||||
|
|
@ -694,7 +695,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
|
|||
|
||||
auto updates = p->get_config_updates(old_slic3r_version);
|
||||
if (updates.incompats.size() > 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1% bundles incompatible. Asking for action...") % updates.incompats.size();
|
||||
BOOST_LOG_TRIVIAL(info) << format("%1% bundles incompatible. Asking for action...", updates.incompats.size());
|
||||
|
||||
std::unordered_map<std::string, wxString> incompats_map;
|
||||
for (const auto &incompat : updates.incompats) {
|
||||
|
|
@ -702,15 +703,14 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
|
|||
const auto max_slic3r = incompat.version.max_slic3r_version;
|
||||
wxString restrictions;
|
||||
if (min_slic3r != Semver::zero() && max_slic3r != Semver::inf()) {
|
||||
restrictions = GUI::from_u8((boost::format(_utf8(L("requires min. %s and max. %s")))
|
||||
% min_slic3r.to_string()
|
||||
% max_slic3r.to_string()).str()
|
||||
);
|
||||
restrictions = GUI::format_wxstr(_L("requires min. %s and max. %s"),
|
||||
min_slic3r.to_string(),
|
||||
max_slic3r.to_string());
|
||||
} else if (min_slic3r != Semver::zero()) {
|
||||
restrictions = GUI::from_u8((boost::format(_utf8(L("requires min. %s"))) % min_slic3r.to_string()).str());
|
||||
restrictions = GUI::format_wxstr(_L("requires min. %s"), min_slic3r.to_string());
|
||||
BOOST_LOG_TRIVIAL(debug) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen.";
|
||||
} else {
|
||||
restrictions = GUI::from_u8((boost::format(_utf8(L("requires max. %s"))) % max_slic3r.to_string()).str());
|
||||
restrictions = GUI::format_wxstr(_L("requires max. %s"), max_slic3r.to_string());
|
||||
}
|
||||
|
||||
incompats_map.emplace(std::make_pair(incompat.vendor, std::move(restrictions)));
|
||||
|
|
@ -742,13 +742,13 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
|
|||
for (const auto& update : updates.updates) {
|
||||
incompatible_version = (update.forced_update ? true : incompatible_version);
|
||||
//td::cout << update.forced_update << std::endl;
|
||||
//BOOST_LOG_TRIVIAL(info) << boost::format("Update requires higher version.");
|
||||
//BOOST_LOG_TRIVIAL(info) << format("Update requires higher version.");
|
||||
}
|
||||
|
||||
//forced update
|
||||
if(incompatible_version)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. At least one requires higher version of Slicer.") % updates.updates.size();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. At least one requires higher version of Slicer.", updates.updates.size());
|
||||
|
||||
std::vector<GUI::MsgUpdateForced::Update> updates_msg;
|
||||
for (const auto& update : updates.updates) {
|
||||
|
|
@ -778,7 +778,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
|
|||
}
|
||||
|
||||
// regular update
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", updates.updates.size());
|
||||
|
||||
std::vector<GUI::MsgUpdateConfig::Update> updates_msg;
|
||||
for (const auto &update : updates.updates) {
|
||||
|
|
@ -813,7 +813,7 @@ void PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool
|
|||
{
|
||||
Updates updates;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Installing %1% bundles from resources ...") % bundles.size();
|
||||
BOOST_LOG_TRIVIAL(info) << format("Installing %1% bundles from resources ...", bundles.size());
|
||||
|
||||
for (const auto &bundle : bundles) {
|
||||
auto path_in_rsrc = (p->rsrc_path / bundle).replace_extension(".ini");
|
||||
|
|
|
|||
|
|
@ -183,3 +183,7 @@
|
|||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "GUI/format.hpp"
|
||||
#endif // _WIN32
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue