mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-26 18:21:18 -06:00
Merge remote-tracking branch 'origin/master' into ys_aliases
This commit is contained in:
commit
afb8483250
51 changed files with 15585 additions and 487 deletions
|
|
@ -136,6 +136,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/ProgressStatusBar.cpp
|
||||
GUI/PrintHostDialogs.cpp
|
||||
GUI/PrintHostDialogs.hpp
|
||||
GUI/Mouse3DController.cpp
|
||||
GUI/Mouse3DController.hpp
|
||||
Utils/Http.cpp
|
||||
Utils/Http.hpp
|
||||
Utils/FixModelByWin10.cpp
|
||||
|
|
@ -170,7 +172,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
|||
|
||||
encoding_check(libslic3r_gui)
|
||||
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES})
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} hidapi)
|
||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -271,6 +271,80 @@ 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)
|
||||
{
|
||||
std::string key = std::string("mouse_device:") + name;
|
||||
auto it = m_storage.find(key);
|
||||
if (it == m_storage.end())
|
||||
it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type(key, std::map<std::string, std::string>())).first;
|
||||
|
||||
it->second.clear();
|
||||
it->second["translation_speed"] = std::to_string(translation_speed);
|
||||
it->second["translation_deadzone"] = std::to_string(translation_deadzone);
|
||||
it->second["rotation_speed"] = std::to_string(rotation_speed);
|
||||
it->second["rotation_deadzone"] = std::to_string(rotation_deadzone);
|
||||
}
|
||||
|
||||
bool AppConfig::get_mouse_device_translation_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("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;
|
||||
}
|
||||
|
||||
void AppConfig::update_config_dir(const std::string &dir)
|
||||
{
|
||||
this->set("recent", "config_directory", dir);
|
||||
|
|
|
|||
|
|
@ -131,8 +131,15 @@ 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);
|
||||
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);
|
||||
|
||||
static const std::string SECTION_FILAMENTS;
|
||||
static const std::string SECTION_MATERIALS;
|
||||
|
||||
private:
|
||||
// Map of section, name -> value
|
||||
std::map<std::string, std::map<std::string, std::string>> m_storage;
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||
#include "libslic3r/GCode/PreviewData.hpp"
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include <cassert>
|
||||
|
|
@ -91,7 +88,7 @@ void BackgroundSlicingProcess::process_fff()
|
|||
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_data);
|
||||
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
|
||||
|
|
@ -139,9 +136,12 @@ void BackgroundSlicingProcess::process_sla()
|
|||
m_sla_print->export_raster(zipper);
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
if (m_thumbnail_data != nullptr)
|
||||
if (m_thumbnail_cb != nullptr)
|
||||
{
|
||||
for (const ThumbnailData& data : *m_thumbnail_data)
|
||||
ThumbnailsList thumbnails;
|
||||
m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false);
|
||||
// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, false, false); // renders also supports and pad
|
||||
for (const ThumbnailData& data : thumbnails)
|
||||
{
|
||||
if (data.is_valid())
|
||||
write_thumbnail(zipper, data);
|
||||
|
|
@ -461,9 +461,12 @@ 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_data != nullptr)
|
||||
if (m_thumbnail_cb != nullptr)
|
||||
{
|
||||
for (const ThumbnailData& data : *m_thumbnail_data)
|
||||
ThumbnailsList thumbnails;
|
||||
m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false);
|
||||
// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, false, false); // renders also supports and pad
|
||||
for (const ThumbnailData& data : thumbnails)
|
||||
{
|
||||
if (data.is_valid())
|
||||
write_thumbnail(zipper, data);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ namespace Slic3r {
|
|||
|
||||
class DynamicPrintConfig;
|
||||
class GCodePreviewData;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
struct ThumbnailData;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
class Model;
|
||||
class SLAPrint;
|
||||
|
||||
|
|
@ -53,7 +50,7 @@ public:
|
|||
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_data(const std::vector<ThumbnailData>* data) { m_thumbnail_data = data; }
|
||||
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
// The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
|
||||
|
|
@ -159,8 +156,8 @@ private:
|
|||
// Data structure, to which the G-code export writes its annotations.
|
||||
GCodePreviewData *m_gcode_preview_data = nullptr;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
// Data structures, used to write thumbnails into gcode.
|
||||
const std::vector<ThumbnailData>* m_thumbnail_data = nullptr;
|
||||
// Callback function, used to write thumbnails into gcode.
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -91,10 +91,16 @@ void Camera::select_next_type()
|
|||
|
||||
void Camera::set_target(const Vec3d& target)
|
||||
{
|
||||
m_target = target;
|
||||
m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0));
|
||||
m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1));
|
||||
m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2));
|
||||
BoundingBoxf3 test_box = m_scene_box;
|
||||
test_box.translate(-m_scene_box.center());
|
||||
// We may let this factor be customizable
|
||||
static const double ScaleFactor = 1.5;
|
||||
test_box.scale(ScaleFactor);
|
||||
test_box.translate(m_scene_box.center());
|
||||
|
||||
m_target(0) = clamp(test_box.min(0), test_box.max(0), target(0));
|
||||
m_target(1) = clamp(test_box.min(1), test_box.max(1), target(1));
|
||||
m_target(2) = clamp(test_box.min(2), test_box.max(2), target(2));
|
||||
}
|
||||
|
||||
void Camera::set_theta(float theta, bool apply_limit)
|
||||
|
|
@ -109,20 +115,20 @@ void Camera::set_theta(float theta, bool apply_limit)
|
|||
}
|
||||
}
|
||||
|
||||
void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h)
|
||||
void Camera::update_zoom(double delta_zoom)
|
||||
{
|
||||
zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0;
|
||||
zoom = m_zoom / (1.0 - zoom);
|
||||
set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1));
|
||||
}
|
||||
|
||||
void Camera::set_zoom(double zoom)
|
||||
{
|
||||
// Don't allow to zoom too far outside the scene.
|
||||
double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h);
|
||||
double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]);
|
||||
if (zoom_min > 0.0)
|
||||
zoom = std::max(zoom, zoom_min * 0.7);
|
||||
|
||||
// Don't allow to zoom too close to the scene.
|
||||
zoom = std::min(zoom, 100.0);
|
||||
|
||||
m_zoom = zoom;
|
||||
m_zoom = std::min(zoom, 100.0);
|
||||
}
|
||||
|
||||
bool Camera::select_view(const std::string& direction)
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ public:
|
|||
void set_theta(float theta, bool apply_limit);
|
||||
|
||||
double get_zoom() const { return m_zoom; }
|
||||
void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h);
|
||||
void set_zoom(double zoom) { m_zoom = zoom; }
|
||||
void update_zoom(double delta_zoom);
|
||||
void set_zoom(double zoom);
|
||||
|
||||
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
|
||||
void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "Mouse3DController.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
|
|
@ -130,6 +131,9 @@ GLCanvas3D::LayersEditing::LayersEditing()
|
|||
, m_object_max_z(0.f)
|
||||
, m_slicing_parameters(nullptr)
|
||||
, m_layer_height_profile_modified(false)
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
, m_adaptive_cusp(0.2f)
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
, state(Unknown)
|
||||
, band_width(2.0f)
|
||||
, strength(0.005f)
|
||||
|
|
@ -150,7 +154,9 @@ GLCanvas3D::LayersEditing::~LayersEditing()
|
|||
}
|
||||
|
||||
const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f;
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
|
||||
{
|
||||
|
|
@ -217,13 +223,103 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
|||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static const ImVec4 orange(0.757f, 0.404f, 0.216f, 1.0f);
|
||||
|
||||
const Size& cnv_size = canvas.get_canvas_size();
|
||||
float canvas_w = (float)cnv_size.get_width();
|
||||
float canvas_h = (float)cnv_size.get_height();
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f);
|
||||
imgui.set_next_window_bg_alpha(0.5f);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
|
||||
imgui.begin(_(L("Layer height profile")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||
imgui.text(_(L("Left mouse button:")));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
imgui.text(_(L("Add detail")));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||
imgui.text(_(L("Right mouse button:")));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
imgui.text(_(L("Remove detail")));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||
imgui.text(_(L("Shift + Left mouse button:")));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
imgui.text(_(L("Reset to base")));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||
imgui.text(_(L("Shift + Right mouse button:")));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
imgui.text(_(L("Smoothing")));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||
imgui.text(_(L("Mouse wheel:")));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
imgui.text(_(L("Increase/decrease edit area")));
|
||||
|
||||
ImGui::Separator();
|
||||
if (imgui.button(_(L("Adaptive"))))
|
||||
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event<float>(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp));
|
||||
|
||||
ImGui::SameLine();
|
||||
float text_align = ImGui::GetCursorPosX();
|
||||
imgui.text(_(L("Cusp (mm)")));
|
||||
ImGui::SameLine();
|
||||
float widget_align = ImGui::GetCursorPosX();
|
||||
ImGui::PushItemWidth(120.0f);
|
||||
m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height);
|
||||
ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f");
|
||||
|
||||
ImGui::Separator();
|
||||
if (imgui.button(_(L("Smooth"))))
|
||||
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params ));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(text_align);
|
||||
imgui.text(_(L("Radius")));
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(120.0f);
|
||||
ImGui::SetCursorPosX(widget_align);
|
||||
int radius = (int)m_smooth_params.radius;
|
||||
if (ImGui::SliderInt("##1", &radius, 1, 10))
|
||||
m_smooth_params.radius = (unsigned int)radius;
|
||||
|
||||
ImGui::SetCursorPosX(text_align);
|
||||
imgui.text(_(L("Keep min")));
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(120.0f);
|
||||
ImGui::SetCursorPosX(widget_align);
|
||||
imgui.checkbox("##2", m_smooth_params.keep_min);
|
||||
|
||||
ImGui::Separator();
|
||||
if (imgui.button(_(L("Reset"))))
|
||||
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
|
||||
|
||||
imgui.end();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
||||
#else
|
||||
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
||||
const Rect& reset_rect = get_reset_rect_viewport(canvas);
|
||||
|
||||
_render_tooltip_texture(canvas, bar_rect, reset_rect);
|
||||
_render_reset_texture(reset_rect);
|
||||
_render_active_object_annotations(canvas, bar_rect);
|
||||
_render_profile(bar_rect);
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
render_active_object_annotations(canvas, bar_rect);
|
||||
render_profile(bar_rect);
|
||||
}
|
||||
|
||||
float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
|
||||
|
|
@ -248,11 +344,13 @@ bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, floa
|
|||
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
|
||||
}
|
||||
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y)
|
||||
{
|
||||
const Rect& rect = get_reset_rect_screen(canvas);
|
||||
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
|
||||
}
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
||||
{
|
||||
|
|
@ -260,9 +358,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
|||
float w = (float)cnv_size.get_width();
|
||||
float h = (float)cnv_size.get_height();
|
||||
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h);
|
||||
#else
|
||||
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas));
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
}
|
||||
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
|
||||
{
|
||||
const Size& cnv_size = canvas.get_canvas_size();
|
||||
|
|
@ -271,6 +374,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
|
|||
|
||||
return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h);
|
||||
}
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
||||
{
|
||||
|
|
@ -281,9 +385,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
|||
float zoom = (float)canvas.get_camera().get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
||||
#else
|
||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom);
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
}
|
||||
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas)
|
||||
{
|
||||
const Size& cnv_size = canvas.get_canvas_size();
|
||||
|
|
@ -295,13 +404,14 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas
|
|||
|
||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
||||
}
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
|
||||
bool GLCanvas3D::LayersEditing::_is_initialized() const
|
||||
bool GLCanvas3D::LayersEditing::is_initialized() const
|
||||
{
|
||||
return m_shader.is_initialized();
|
||||
}
|
||||
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const
|
||||
{
|
||||
// TODO: do this with ImGui
|
||||
|
|
@ -347,8 +457,9 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co
|
|||
|
||||
GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top());
|
||||
}
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const
|
||||
void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const
|
||||
{
|
||||
m_shader.start_using();
|
||||
|
||||
|
|
@ -379,7 +490,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
|
|||
m_shader.stop_using();
|
||||
}
|
||||
|
||||
void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const
|
||||
void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const
|
||||
{
|
||||
//FIXME show some kind of legend.
|
||||
|
||||
|
|
@ -496,6 +607,24 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas)
|
|||
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp)
|
||||
{
|
||||
m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp);
|
||||
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
|
||||
m_layers_texture.valid = false;
|
||||
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
||||
void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params)
|
||||
{
|
||||
m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params);
|
||||
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
|
||||
m_layers_texture.valid = false;
|
||||
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
void GLCanvas3D::LayersEditing::generate_layer_height_texture()
|
||||
{
|
||||
this->update_slicing_parameters();
|
||||
|
|
@ -557,6 +686,7 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas)
|
|||
* THICKNESS_BAR_WIDTH;
|
||||
}
|
||||
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas)
|
||||
{
|
||||
return
|
||||
|
|
@ -567,6 +697,7 @@ float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas)
|
|||
#endif
|
||||
* THICKNESS_RESET_BUTTON_HEIGHT;
|
||||
}
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
|
||||
const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX);
|
||||
|
|
@ -1118,6 +1249,11 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
|||
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
|
||||
|
|
@ -1372,7 +1508,7 @@ void GLCanvas3D::set_model(Model* model)
|
|||
|
||||
void GLCanvas3D::bed_shape_changed()
|
||||
{
|
||||
m_camera.set_scene_box(scene_bounding_box());
|
||||
refresh_camera_scene_box();
|
||||
m_camera.requires_zoom_to_bed = true;
|
||||
m_dirty = true;
|
||||
if (m_bed.is_prusa())
|
||||
|
|
@ -1398,7 +1534,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
|
|||
BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb = volumes_bounding_box();
|
||||
bb.merge(m_bed.get_bounding_box(false));
|
||||
bb.merge(m_bed.get_bounding_box(true));
|
||||
|
||||
if (m_config != nullptr)
|
||||
{
|
||||
|
|
@ -1420,6 +1556,29 @@ bool GLCanvas3D::is_layers_editing_allowed() const
|
|||
return m_layers_editing.is_allowed();
|
||||
}
|
||||
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
void GLCanvas3D::reset_layer_height_profile()
|
||||
{
|
||||
m_layers_editing.reset_layer_height_profile(*this);
|
||||
m_layers_editing.state = LayersEditing::Completed;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::adaptive_layer_height_profile(float cusp)
|
||||
{
|
||||
m_layers_editing.adaptive_layer_height_profile(*this, cusp);
|
||||
m_layers_editing.state = LayersEditing::Completed;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params)
|
||||
{
|
||||
m_layers_editing.smooth_layer_height_profile(*this, smoothing_params);
|
||||
m_layers_editing.state = LayersEditing::Completed;
|
||||
m_dirty = true;
|
||||
}
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
bool GLCanvas3D::is_reload_delayed() const
|
||||
{
|
||||
return m_reload_delayed;
|
||||
|
|
@ -1546,10 +1705,11 @@ void GLCanvas3D::render()
|
|||
return;
|
||||
}
|
||||
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
|
||||
if (m_camera.requires_zoom_to_bed)
|
||||
{
|
||||
zoom_to_bed();
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
||||
m_camera.requires_zoom_to_bed = false;
|
||||
}
|
||||
|
|
@ -1640,6 +1800,8 @@ void GLCanvas3D::render()
|
|||
m_camera.debug_render();
|
||||
#endif // ENABLE_CAMERA_STATISTICS
|
||||
|
||||
wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
||||
|
||||
wxGetApp().imgui()->render();
|
||||
|
||||
m_canvas->SwapBuffers();
|
||||
|
|
@ -2098,7 +2260,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
|
||||
}
|
||||
|
||||
m_camera.set_scene_box(scene_bounding_box());
|
||||
refresh_camera_scene_box();
|
||||
|
||||
if (m_selection.is_empty())
|
||||
{
|
||||
|
|
@ -2134,12 +2296,12 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
|
|||
|
||||
static void load_gcode_retractions(const GCodePreviewData::Retraction& retractions, GLCanvas3D::GCodePreviewVolumeIndex::EType extrusion_type, GLVolumeCollection &volumes, GLCanvas3D::GCodePreviewVolumeIndex &volume_index, bool gl_initialized)
|
||||
{
|
||||
volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size());
|
||||
|
||||
// nothing to render, return
|
||||
if (retractions.positions.empty())
|
||||
return;
|
||||
|
||||
volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size());
|
||||
|
||||
GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba, VERTEX_BUFFER_RESERVE_SIZE);
|
||||
|
||||
GCodePreviewData::Retraction::PositionsList copy(retractions.positions);
|
||||
|
|
@ -2205,6 +2367,9 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
|
|||
++ idx_volume_index_src;
|
||||
idx_volume_of_this_type_last = (idx_volume_index_src + 1 == m_gcode_preview_volume_index.first_volumes.size()) ? m_volumes.volumes.size() : m_gcode_preview_volume_index.first_volumes[idx_volume_index_src + 1].id;
|
||||
idx_volume_of_this_type_first_new = idx_volume_dst;
|
||||
if (idx_volume_src == idx_volume_of_this_type_last)
|
||||
// Empty sequence of volumes for the current index item.
|
||||
continue;
|
||||
}
|
||||
if (! m_volumes.volumes[idx_volume_src]->print_zs.empty())
|
||||
m_volumes.volumes[idx_volume_dst ++] = m_volumes.volumes[idx_volume_src];
|
||||
|
|
@ -2338,14 +2503,21 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||
m_dirty |= m_main_toolbar.update_items_state();
|
||||
m_dirty |= m_undoredo_toolbar.update_items_state();
|
||||
m_dirty |= m_view_toolbar.update_items_state();
|
||||
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera);
|
||||
m_dirty |= mouse3d_controller_applied;
|
||||
|
||||
if (!m_dirty)
|
||||
return;
|
||||
|
||||
_refresh_if_shown_on_screen();
|
||||
|
||||
if (m_keep_dirty)
|
||||
if (m_keep_dirty || mouse3d_controller_applied)
|
||||
{
|
||||
m_dirty = true;
|
||||
evt.RequestMore();
|
||||
}
|
||||
else
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
|
|
@ -2390,6 +2562,20 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
#endif /* __APPLE__ */
|
||||
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
|
||||
break;
|
||||
|
||||
#ifdef __APPLE__
|
||||
case 'm':
|
||||
case 'M':
|
||||
#else /* __APPLE__ */
|
||||
case WXK_CONTROL_M:
|
||||
#endif /* __APPLE__ */
|
||||
{
|
||||
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
|
||||
controller.show_settings_dialog(!controller.is_settings_dialog_shown());
|
||||
m_dirty = true;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
case 'v':
|
||||
case 'V':
|
||||
|
|
@ -2457,11 +2643,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
case 'B':
|
||||
case 'b': { zoom_to_bed(); break; }
|
||||
case 'I':
|
||||
case 'i': { set_camera_zoom(1.0); break; }
|
||||
case 'i': { _update_camera_zoom(1.0); break; }
|
||||
case 'K':
|
||||
case 'k': { m_camera.select_next_type(); m_dirty = true; break; }
|
||||
case 'O':
|
||||
case 'o': { set_camera_zoom(-1.0); break; }
|
||||
case 'o': { _update_camera_zoom(-1.0); break; }
|
||||
#if ENABLE_RENDER_PICKING_PASS
|
||||
case 'T':
|
||||
case 't': {
|
||||
|
|
@ -2564,6 +2750,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
|
||||
void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
{
|
||||
// try to filter out events coming from mouse 3d
|
||||
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
|
||||
if (controller.process_mouse_wheel())
|
||||
return;
|
||||
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
|
|
@ -2608,7 +2799,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
|||
return;
|
||||
|
||||
// Calculate the zoom delta and apply it to the current zoom factor
|
||||
set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
|
||||
_update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_timer(wxTimerEvent& evt)
|
||||
|
|
@ -2800,6 +2991,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
m_layers_editing.state = LayersEditing::Editing;
|
||||
_perform_layer_editing_action(&evt);
|
||||
}
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1)))
|
||||
{
|
||||
if (evt.LeftDown())
|
||||
|
|
@ -2812,6 +3004,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled)
|
||||
{
|
||||
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)
|
||||
|
|
@ -3391,13 +3584,6 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
|||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_camera_zoom(double zoom)
|
||||
{
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height());
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::update_gizmos_on_off_state()
|
||||
{
|
||||
set_as_dirty();
|
||||
|
|
@ -4183,8 +4369,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
|||
|
||||
// updates camera
|
||||
m_camera.apply_viewport(0, 0, w, h);
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const
|
||||
|
|
@ -4221,6 +4405,12 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box)
|
|||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
void GLCanvas3D::_update_camera_zoom(double zoom)
|
||||
{
|
||||
m_camera.update_zoom(zoom);
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::_refresh_if_shown_on_screen()
|
||||
{
|
||||
if (_is_shown_on_screen())
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
|
|||
using Vec3dEvent = Event<Vec3d>;
|
||||
template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
|
||||
|
||||
using HeightProfileSmoothEvent = Event<HeightProfileSmoothingParams>;
|
||||
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
||||
|
|
@ -104,6 +106,11 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
class GLCanvas3D
|
||||
{
|
||||
|
|
@ -153,13 +160,17 @@ private:
|
|||
|
||||
private:
|
||||
static const float THICKNESS_BAR_WIDTH;
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static const float THICKNESS_RESET_BUTTON_HEIGHT;
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
bool m_enabled;
|
||||
Shader m_shader;
|
||||
unsigned int m_z_texture_id;
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
mutable GLTexture m_tooltip_texture;
|
||||
mutable GLTexture m_reset_texture;
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
// Not owned by LayersEditing.
|
||||
const DynamicPrintConfig *m_config;
|
||||
// ModelObject for the currently selected object (Model::objects[last_object_id]).
|
||||
|
|
@ -171,6 +182,11 @@ private:
|
|||
std::vector<coordf_t> m_layer_height_profile;
|
||||
bool m_layer_height_profile_modified;
|
||||
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
mutable float m_adaptive_cusp;
|
||||
mutable HeightProfileSmoothingParams m_smooth_params;
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
class LayersTexture
|
||||
{
|
||||
public:
|
||||
|
|
@ -217,28 +233,42 @@ private:
|
|||
void adjust_layer_height_profile();
|
||||
void accept_changes(GLCanvas3D& canvas);
|
||||
void reset_layer_height_profile(GLCanvas3D& canvas);
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp);
|
||||
void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn);
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
static float get_cursor_z_relative(const GLCanvas3D& canvas);
|
||||
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static Rect get_reset_rect_screen(const GLCanvas3D& canvas);
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static Rect get_reset_rect_viewport(const GLCanvas3D& canvas);
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
float object_max_z() const { return m_object_max_z; }
|
||||
|
||||
private:
|
||||
bool _is_initialized() const;
|
||||
bool is_initialized() const;
|
||||
void generate_layer_height_texture();
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const;
|
||||
void _render_reset_texture(const Rect& reset_rect) const;
|
||||
void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const;
|
||||
void _render_profile(const Rect& bar_rect) const;
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
void render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const;
|
||||
void render_profile(const Rect& bar_rect) const;
|
||||
void update_slicing_parameters();
|
||||
|
||||
static float thickness_bar_width(const GLCanvas3D &canvas);
|
||||
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
static float reset_button_height(const GLCanvas3D &canvas);
|
||||
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
};
|
||||
|
||||
struct Mouse
|
||||
|
|
@ -493,6 +523,7 @@ public:
|
|||
void set_color_by(const std::string& value);
|
||||
|
||||
const Camera& get_camera() const { return m_camera; }
|
||||
Camera& get_camera() { return m_camera; }
|
||||
|
||||
BoundingBoxf3 volumes_bounding_box() const;
|
||||
BoundingBoxf3 scene_bounding_box() const;
|
||||
|
|
@ -500,6 +531,12 @@ public:
|
|||
bool is_layers_editing_enabled() const;
|
||||
bool is_layers_editing_allowed() const;
|
||||
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
void reset_layer_height_profile();
|
||||
void adaptive_layer_height_profile(float cusp);
|
||||
void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params);
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
bool is_reload_delayed() const;
|
||||
|
||||
void enable_layers_editing(bool enable);
|
||||
|
|
@ -576,8 +613,6 @@ public:
|
|||
void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
|
||||
void do_mirror(const std::string& snapshot_type);
|
||||
|
||||
void set_camera_zoom(double zoom);
|
||||
|
||||
void update_gizmos_on_off_state();
|
||||
void reset_all_gizmos() { m_gizmos.reset_all_states(); }
|
||||
|
||||
|
|
@ -655,6 +690,7 @@ private:
|
|||
#else
|
||||
void _zoom_to_box(const BoundingBoxf3& box);
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
void _update_camera_zoom(double zoom);
|
||||
|
||||
void _refresh_if_shown_on_screen();
|
||||
|
||||
|
|
|
|||
|
|
@ -1126,7 +1126,6 @@ void GUI_App::gcode_thumbnails_debug()
|
|||
}
|
||||
else if (reading_image && boost::starts_with(gcode_line, END_MASK))
|
||||
{
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE
|
||||
std::string out_filename = out_path + std::to_string(width) + "x" + std::to_string(height) + ".png";
|
||||
boost::nowide::ofstream out_file(out_filename.c_str(), std::ios::binary);
|
||||
if (out_file.good())
|
||||
|
|
@ -1138,46 +1137,6 @@ void GUI_App::gcode_thumbnails_debug()
|
|||
out_file.write(decoded.c_str(), decoded.size());
|
||||
out_file.close();
|
||||
}
|
||||
#else
|
||||
if (!row.empty())
|
||||
{
|
||||
rows.push_back(row);
|
||||
row.clear();
|
||||
}
|
||||
|
||||
if ((unsigned int)rows.size() == height)
|
||||
{
|
||||
std::vector<unsigned char> thumbnail(4 * width * height, 0);
|
||||
for (unsigned int r = 0; r < (unsigned int)rows.size(); ++r)
|
||||
{
|
||||
std::string decoded_row;
|
||||
decoded_row.resize(boost::beast::detail::base64::decoded_size(rows[r].size()));
|
||||
decoded_row.resize(boost::beast::detail::base64::decode((void*)&decoded_row[0], rows[r].data(), rows[r].size()).first);
|
||||
|
||||
if ((unsigned int)decoded_row.size() == width * 4)
|
||||
{
|
||||
void* image_ptr = (void*)(thumbnail.data() + r * width * 4);
|
||||
::memcpy(image_ptr, (const void*)decoded_row.c_str(), width * 4);
|
||||
}
|
||||
}
|
||||
|
||||
wxImage image(width, height);
|
||||
image.InitAlpha();
|
||||
|
||||
for (unsigned int r = 0; r < height; ++r)
|
||||
{
|
||||
unsigned int rr = r * width;
|
||||
for (unsigned int c = 0; c < width; ++c)
|
||||
{
|
||||
unsigned char* px = thumbnail.data() + 4 * (rr + c);
|
||||
image.SetRGB((int)c, (int)r, px[0], px[1], px[2]);
|
||||
image.SetAlpha((int)c, (int)r, px[3]);
|
||||
}
|
||||
}
|
||||
|
||||
image.SaveFile(out_path + std::to_string(width) + "x" + std::to_string(height) + ".png", wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE
|
||||
|
||||
reading_image = false;
|
||||
width = 0;
|
||||
|
|
@ -1185,17 +1144,7 @@ void GUI_App::gcode_thumbnails_debug()
|
|||
rows.clear();
|
||||
}
|
||||
else if (reading_image)
|
||||
{
|
||||
#if !ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE
|
||||
if (!row.empty() && (gcode_line[1] == ' '))
|
||||
{
|
||||
rows.push_back(row);
|
||||
row.clear();
|
||||
}
|
||||
#endif // !ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE
|
||||
|
||||
row += gcode_line.substr(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -528,6 +528,9 @@ void ImGuiWrapper::init_style()
|
|||
// Slider
|
||||
set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK);
|
||||
set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT);
|
||||
|
||||
// Separator
|
||||
set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object")));
|
||||
plater_shortcuts.push_back(Shortcut("I", L("Zoom in")));
|
||||
plater_shortcuts.push_back(Shortcut("O", L("Zoom out")));
|
||||
plater_shortcuts.push_back(Shortcut(ctrl+"M", L("Show/Hide 3Dconnexion devices settings dialog")));
|
||||
plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection")));
|
||||
#if ENABLE_RENDER_PICKING_PASS
|
||||
// Don't localize debugging texts.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "PrintHostDialogs.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "Mouse3DController.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
|
|
|||
822
src/slic3r/GUI/Mouse3DController.cpp
Normal file
822
src/slic3r/GUI/Mouse3DController.cpp
Normal file
|
|
@ -0,0 +1,822 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "Mouse3DController.hpp"
|
||||
|
||||
#include "Camera.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
|
||||
#include <wx/glcanvas.h>
|
||||
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules
|
||||
|
||||
static const std::vector<int> _3DCONNEXION_VENDORS =
|
||||
{
|
||||
0x046d, // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech)
|
||||
0x256F // 3DCONNECTION = 9583 // 3Dconnexion
|
||||
};
|
||||
|
||||
// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202
|
||||
static const std::vector<int> _3DCONNEXION_DEVICES =
|
||||
{
|
||||
0xc603, /* 50691 spacemouse plus XT */
|
||||
0xc605, /* 50693 cadman */
|
||||
0xc606, /* 50694 spacemouse classic */
|
||||
0xc621, /* 50721 spaceball 5000 */
|
||||
0xc623, /* 50723 space traveller */
|
||||
0xc625, /* 50725 space pilot */
|
||||
0xc626, /* 50726 space navigator *TESTED* */
|
||||
0xc627, /* 50727 space explorer */
|
||||
0xc628, /* 50728 space navigator for notebooks*/
|
||||
0xc629, /* 50729 space pilot pro*/
|
||||
0xc62b, /* 50731 space mouse pro*/
|
||||
0xc62e, /* 50734 spacemouse wireless (USB cable) *TESTED* */
|
||||
0xc62f, /* 50735 spacemouse wireless receiver */
|
||||
0xc631, /* 50737 spacemouse pro wireless *TESTED* */
|
||||
0xc632, /* 50738 spacemouse pro wireless receiver */
|
||||
0xc633, /* 50739 spacemouse enterprise */
|
||||
0xc635, /* 50741 spacemouse compact *TESTED* */
|
||||
0xc636, /* 50742 spacemouse module */
|
||||
0xc640, /* 50752 nulooq */
|
||||
0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */
|
||||
};
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
const double Mouse3DController::State::DefaultTranslationScale = 2.5;
|
||||
const double Mouse3DController::State::MaxTranslationDeadzone = 0.2;
|
||||
const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone;
|
||||
const float Mouse3DController::State::DefaultRotationScale = 1.0f;
|
||||
const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone;
|
||||
const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone;
|
||||
|
||||
Mouse3DController::State::State()
|
||||
: m_buttons_enabled(false)
|
||||
, m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone)
|
||||
, m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone)
|
||||
, m_mouse_wheel_counter(0)
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
, m_translation_queue_max_size(0)
|
||||
, m_rotation_queue_max_size(0)
|
||||
, m_buttons_queue_max_size(0)
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
{
|
||||
}
|
||||
|
||||
void Mouse3DController::State::append_translation(const Vec3d& translation)
|
||||
{
|
||||
while (m_translation.queue.size() >= m_translation.max_size)
|
||||
{
|
||||
m_translation.queue.pop();
|
||||
}
|
||||
m_translation.queue.push(translation);
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
m_translation_queue_max_size = std::max(m_translation_queue_max_size, m_translation.queue.size());
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
void Mouse3DController::State::append_rotation(const Vec3f& rotation)
|
||||
{
|
||||
while (m_rotation.queue.size() >= m_rotation.max_size)
|
||||
{
|
||||
m_rotation.queue.pop();
|
||||
}
|
||||
m_rotation.queue.push(rotation);
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, m_rotation.queue.size());
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
if (rotation(0) != 0.0f)
|
||||
++m_mouse_wheel_counter;
|
||||
}
|
||||
|
||||
void Mouse3DController::State::append_button(unsigned int id)
|
||||
{
|
||||
if (!m_buttons_enabled)
|
||||
return;
|
||||
|
||||
m_buttons.push(id);
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size());
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
bool Mouse3DController::State::process_mouse_wheel()
|
||||
{
|
||||
if (m_mouse_wheel_counter == 0)
|
||||
return false;
|
||||
else if (!m_rotation.queue.empty())
|
||||
{
|
||||
--m_mouse_wheel_counter;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_mouse_wheel_counter = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mouse3DController::State::set_queues_max_size(size_t size)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
m_translation.max_size = size;
|
||||
m_rotation.max_size = size;
|
||||
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
m_translation_queue_max_size = 0;
|
||||
m_rotation_queue_max_size = 0;
|
||||
m_buttons_queue_max_size = 0;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
}
|
||||
}
|
||||
|
||||
bool Mouse3DController::State::apply(Camera& camera)
|
||||
{
|
||||
if (!wxGetApp().IsActive())
|
||||
return false;
|
||||
|
||||
bool ret = false;
|
||||
|
||||
if (has_translation())
|
||||
{
|
||||
const Vec3d& translation = m_translation.queue.front();
|
||||
camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up()));
|
||||
m_translation.queue.pop();
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (has_rotation())
|
||||
{
|
||||
const Vec3f& rotation = m_rotation.queue.front();
|
||||
float theta = m_rotation_params.scale * rotation(0);
|
||||
float phi = m_rotation_params.scale * rotation(2);
|
||||
float sign = camera.inverted_phi ? -1.0f : 1.0f;
|
||||
camera.phi += sign * phi;
|
||||
camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA);
|
||||
m_rotation.queue.pop();
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (m_buttons_enabled && has_button())
|
||||
{
|
||||
unsigned int button = m_buttons.front();
|
||||
switch (button)
|
||||
{
|
||||
case 0: { camera.update_zoom(1.0); break; }
|
||||
case 1: { camera.update_zoom(-1.0); break; }
|
||||
default: { break; }
|
||||
}
|
||||
m_buttons.pop();
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Mouse3DController::Mouse3DController()
|
||||
: m_initialized(false)
|
||||
, m_device(nullptr)
|
||||
, m_device_str("")
|
||||
, m_running(false)
|
||||
, m_settings_dialog(false)
|
||||
{
|
||||
m_last_time = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void Mouse3DController::init()
|
||||
{
|
||||
if (m_initialized)
|
||||
return;
|
||||
|
||||
// Initialize the hidapi library
|
||||
int res = hid_init();
|
||||
if (res != 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library";
|
||||
return;
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void Mouse3DController::shutdown()
|
||||
{
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
stop();
|
||||
disconnect_device();
|
||||
|
||||
// Finalize the hidapi library
|
||||
hid_exit();
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
bool Mouse3DController::apply(Camera& camera)
|
||||
{
|
||||
if (!m_initialized)
|
||||
return false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
// check if the user unplugged the device
|
||||
if (!m_running && is_device_connected())
|
||||
{
|
||||
disconnect_device();
|
||||
// hides the settings dialog if the user re-plug the device
|
||||
m_settings_dialog = false;
|
||||
}
|
||||
|
||||
// check if the user plugged the device
|
||||
if (connect_device())
|
||||
start();
|
||||
|
||||
return is_device_connected() ? m_state.apply(camera) : false;
|
||||
}
|
||||
|
||||
void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const
|
||||
{
|
||||
if (!m_running || !m_settings_dialog)
|
||||
return;
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
|
||||
imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f);
|
||||
imgui.set_next_window_bg_alpha(0.5f);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
|
||||
imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
imgui.text(_(L("Device:")));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
imgui.text(m_device_str);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
imgui.text(_(L("Speed:")));
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale;
|
||||
if (ImGui::SliderFloat(_(L("Translation##1")), &translation_scale, 0.5f, 2.0f, "%.1f"))
|
||||
m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
|
||||
|
||||
float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale;
|
||||
if (ImGui::SliderFloat(_(L("Rotation##1")), &rotation_scale, 0.5f, 2.0f, "%.1f"))
|
||||
m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
imgui.text(_(L("Deadzone:")));
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
float translation_deadzone = (float)m_state.get_translation_deadzone();
|
||||
if (ImGui::SliderFloat(_(L("Translation##2")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
|
||||
m_state.set_translation_deadzone((double)translation_deadzone);
|
||||
|
||||
float rotation_deadzone = m_state.get_rotation_deadzone();
|
||||
if (ImGui::SliderFloat(_(L("Rotation##2")), &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f"))
|
||||
m_state.set_rotation_deadzone(rotation_deadzone);
|
||||
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
ImGui::Separator();
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
imgui.text("DEBUG:");
|
||||
imgui.text("Vectors:");
|
||||
ImGui::PopStyleColor();
|
||||
Vec3f translation = m_state.get_translation().cast<float>();
|
||||
Vec3f rotation = m_state.get_rotation();
|
||||
ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
imgui.text("Queue size:");
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() };
|
||||
int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() };
|
||||
int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() };
|
||||
|
||||
ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
int queue_size = (int)m_state.get_queues_max_size();
|
||||
if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly))
|
||||
{
|
||||
if (queue_size > 0)
|
||||
m_state.set_queues_max_size(queue_size);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
imgui.text("Camera:");
|
||||
ImGui::PopStyleColor();
|
||||
Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>();
|
||||
ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
|
||||
imgui.end();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
bool Mouse3DController::connect_device()
|
||||
{
|
||||
static const long long DETECTION_TIME = 2; // seconds
|
||||
|
||||
if (is_device_connected())
|
||||
return false;
|
||||
|
||||
// check time since last detection took place
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - m_last_time).count() < DETECTION_TIME)
|
||||
return false;
|
||||
|
||||
m_last_time = now;
|
||||
|
||||
// Enumerates devices
|
||||
hid_device_info* devices = hid_enumerate(0, 0);
|
||||
if (devices == nullptr)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Searches for 1st connected 3Dconnexion device
|
||||
struct DeviceData
|
||||
{
|
||||
std::string path;
|
||||
unsigned short usage_page;
|
||||
unsigned short usage;
|
||||
|
||||
DeviceData()
|
||||
: path(""), usage_page(0), usage(0)
|
||||
{}
|
||||
DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage)
|
||||
: path(path), usage_page(usage_page), usage(usage)
|
||||
{}
|
||||
|
||||
bool has_valid_usage() const { return (usage_page == 1) && (usage == 8); }
|
||||
};
|
||||
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
hid_device_info* cur = devices;
|
||||
std::cout << std::endl << "======================================================================================================================================" << std::endl;
|
||||
std::cout << "Detected devices:" << std::endl;
|
||||
while (cur != nullptr)
|
||||
{
|
||||
std::cout << "\"";
|
||||
std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown");
|
||||
std::cout << "/";
|
||||
std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown");
|
||||
std::cout << "\" code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")";
|
||||
std::cout << " serial number: '";
|
||||
std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown");
|
||||
std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl;
|
||||
|
||||
cur = cur->next;
|
||||
}
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
|
||||
// When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id.
|
||||
// To choose from them the right one we use:
|
||||
// On Windows and Mac: usage_page == 1 and usage == 8
|
||||
// On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open
|
||||
// When only a single device is detected, as for wired connections, vendor_id and product_id are enough
|
||||
|
||||
// First we count all the valid devices from the enumerated list,
|
||||
|
||||
hid_device_info* current = devices;
|
||||
typedef std::pair<unsigned short, unsigned short> DeviceIds;
|
||||
typedef std::vector<DeviceData> DeviceDataList;
|
||||
typedef std::map<DeviceIds, DeviceDataList> DetectedDevices;
|
||||
DetectedDevices detected_devices;
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << std::endl << "Detected 3D connexion devices:" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
while (current != nullptr)
|
||||
{
|
||||
unsigned short vendor_id = 0;
|
||||
unsigned short product_id = 0;
|
||||
|
||||
for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i)
|
||||
{
|
||||
if (_3DCONNEXION_VENDORS[i] == current->vendor_id)
|
||||
{
|
||||
vendor_id = current->vendor_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vendor_id != 0)
|
||||
{
|
||||
for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i)
|
||||
{
|
||||
if (_3DCONNEXION_DEVICES[i] == current->product_id)
|
||||
{
|
||||
product_id = current->product_id;
|
||||
DeviceIds detected_device(vendor_id, product_id);
|
||||
DetectedDevices::iterator it = detected_devices.find(detected_device);
|
||||
if (it == detected_devices.end())
|
||||
it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first;
|
||||
|
||||
it->second.emplace_back(current->path, current->usage_page, current->usage);
|
||||
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown");
|
||||
std::cout << "/";
|
||||
std::wcout << ((current->product_string != nullptr) ? current->product_string : L"Unknown");
|
||||
std::cout << "\" code: " << current->vendor_id << "/" << current->product_id << " (" << std::hex << current->vendor_id << "/" << current->product_id << std::dec << ")";
|
||||
std::cout << " serial number: '";
|
||||
std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown");
|
||||
std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// Free enumerated devices
|
||||
hid_free_enumeration(devices);
|
||||
|
||||
if (detected_devices.empty())
|
||||
return false;
|
||||
|
||||
std::string path = "";
|
||||
unsigned short vendor_id = 0;
|
||||
unsigned short product_id = 0;
|
||||
|
||||
// Then we'll decide the choosing logic to apply in dependence of the device count and operating system
|
||||
|
||||
for (const DetectedDevices::value_type& device : detected_devices)
|
||||
{
|
||||
if (device.second.size() == 1)
|
||||
{
|
||||
#ifdef __linux__
|
||||
hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr);
|
||||
if (test_device != nullptr)
|
||||
{
|
||||
hid_close(test_device);
|
||||
#else
|
||||
if (device.second.front().has_valid_usage())
|
||||
{
|
||||
#endif // __linux__
|
||||
vendor_id = device.first.first;
|
||||
product_id = device.first.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found = false;
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
for (const DeviceData& data : device.second)
|
||||
{
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\"";
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
#ifdef __linux__
|
||||
hid_device* test_device = hid_open_path(data.path.c_str());
|
||||
if (test_device != nullptr)
|
||||
{
|
||||
path = data.path;
|
||||
vendor_id = device.first.first;
|
||||
product_id = device.first.second;
|
||||
found = true;
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << "-> PASSED" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
hid_close(test_device);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (data.has_valid_usage())
|
||||
{
|
||||
path = data.path;
|
||||
vendor_id = device.first.first;
|
||||
product_id = device.first.second;
|
||||
found = true;
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << "-> PASSED" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
break;
|
||||
}
|
||||
#endif // __linux__
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
else
|
||||
std::cout << "-> NOT PASSED" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
if ((vendor_id != 0) && (product_id != 0))
|
||||
{
|
||||
// Open the 3Dconnexion device using vendor_id and product_id
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << " using hid_open()" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
m_device = hid_open(vendor_id, product_id, nullptr);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Open the 3Dconnexion device using the device path
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << "\"" << path << "\" using hid_open_path()" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
m_device = hid_open_path(path.c_str());
|
||||
}
|
||||
|
||||
if (m_device != nullptr)
|
||||
{
|
||||
std::vector<wchar_t> manufacturer(1024, 0);
|
||||
hid_get_manufacturer_string(m_device, manufacturer.data(), 1024);
|
||||
m_device_str = boost::nowide::narrow(manufacturer.data());
|
||||
|
||||
std::vector<wchar_t> product(1024, 0);
|
||||
hid_get_product_string(m_device, product.data(), 1024);
|
||||
m_device_str += "/" + boost::nowide::narrow(product.data());
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str;
|
||||
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << std::endl << "Connected device:" << std::endl;
|
||||
std::cout << "Manufacturer/product: " << m_device_str << std::endl;
|
||||
std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl;
|
||||
std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl;
|
||||
std::cout << "Path................: '" << path << "'" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
|
||||
// get device parameters from the config, if present
|
||||
double translation_speed = 1.0;
|
||||
float rotation_speed = 1.0;
|
||||
double translation_deadzone = State::DefaultTranslationDeadzone;
|
||||
float rotation_deadzone = State::DefaultRotationDeadzone;
|
||||
wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed);
|
||||
wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone);
|
||||
wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed);
|
||||
wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone);
|
||||
// clamp to valid values
|
||||
m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed)));
|
||||
m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone)));
|
||||
m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed)));
|
||||
m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone)));
|
||||
}
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
else
|
||||
{
|
||||
std::cout << std::endl << "Unable to connect to device:" << std::endl;
|
||||
std::cout << "Manufacturer/product: " << m_device_str << std::endl;
|
||||
std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl;
|
||||
std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl;
|
||||
std::cout << "Path................: '" << path << "'" << std::endl;
|
||||
}
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
|
||||
return (m_device != nullptr);
|
||||
}
|
||||
|
||||
void Mouse3DController::disconnect_device()
|
||||
{
|
||||
if (!is_device_connected())
|
||||
return;
|
||||
|
||||
// Stop the secondary thread, if running
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
|
||||
// Store current device parameters into the config
|
||||
wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(),
|
||||
m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone());
|
||||
wxGetApp().app_config->save();
|
||||
|
||||
// Close the 3Dconnexion device
|
||||
hid_close(m_device);
|
||||
m_device = nullptr;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str;
|
||||
|
||||
m_device_str = "";
|
||||
}
|
||||
|
||||
void Mouse3DController::start()
|
||||
{
|
||||
if (!is_device_connected() || m_running)
|
||||
return;
|
||||
|
||||
m_thread = std::thread(&Mouse3DController::run, this);
|
||||
}
|
||||
|
||||
void Mouse3DController::run()
|
||||
{
|
||||
m_running = true;
|
||||
while (m_running)
|
||||
{
|
||||
collect_input();
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse3DController::collect_input()
|
||||
{
|
||||
DataPacket packet = { 0 };
|
||||
int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100);
|
||||
if (res < 0)
|
||||
{
|
||||
// An error occourred (device detached from pc ?)
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wxGetApp().IsActive())
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
bool updated = false;
|
||||
|
||||
if (res == 7)
|
||||
updated = handle_packet(packet);
|
||||
else if (res == 13)
|
||||
updated = handle_wireless_packet(packet);
|
||||
else if ((res == 3) && (packet[0] == 3))
|
||||
// On Mac button packets can be 3 bytes long
|
||||
updated = handle_packet(packet);
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
else if (res > 0)
|
||||
std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
|
||||
if (updated)
|
||||
// ask for an idle event to update 3D scene
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
|
||||
bool Mouse3DController::handle_packet(const DataPacket& packet)
|
||||
{
|
||||
switch (packet[0])
|
||||
{
|
||||
case 1: // Translation
|
||||
{
|
||||
if (handle_packet_translation(packet))
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: // Rotation
|
||||
{
|
||||
if (handle_packet_rotation(packet, 1))
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: // Button
|
||||
{
|
||||
if (handle_packet_button(packet, packet.size() - 1))
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
case 23: // Battery charge
|
||||
{
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mouse3DController::handle_wireless_packet(const DataPacket& packet)
|
||||
{
|
||||
switch (packet[0])
|
||||
{
|
||||
case 1: // Translation + Rotation
|
||||
{
|
||||
bool updated = handle_packet_translation(packet);
|
||||
updated |= handle_packet_rotation(packet, 7);
|
||||
|
||||
if (updated)
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: // Button
|
||||
{
|
||||
if (handle_packet_button(packet, 12))
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
case 23: // Battery charge
|
||||
{
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl;
|
||||
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
double convert_input(unsigned char first, unsigned char second, double deadzone)
|
||||
{
|
||||
short value = first | second << 8;
|
||||
double ret = (double)value / 350.0;
|
||||
return (std::abs(ret) > deadzone) ? ret : 0.0;
|
||||
}
|
||||
|
||||
bool Mouse3DController::handle_packet_translation(const DataPacket& packet)
|
||||
{
|
||||
double deadzone = m_state.get_translation_deadzone();
|
||||
Vec3d translation(-convert_input(packet[1], packet[2], deadzone),
|
||||
convert_input(packet[3], packet[4], deadzone),
|
||||
convert_input(packet[5], packet[6], deadzone));
|
||||
|
||||
if (!translation.isApprox(Vec3d::Zero()))
|
||||
{
|
||||
m_state.append_translation(translation);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte)
|
||||
{
|
||||
double deadzone = (double)m_state.get_rotation_deadzone();
|
||||
Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone),
|
||||
(float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone),
|
||||
-(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone));
|
||||
|
||||
if (!rotation.isApprox(Vec3f::Zero()))
|
||||
{
|
||||
m_state.append_rotation(rotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size)
|
||||
{
|
||||
unsigned int data = 0;
|
||||
for (unsigned int i = 1; i < packet_size; ++i)
|
||||
{
|
||||
data |= packet[i] << 8 * (i - 1);
|
||||
}
|
||||
|
||||
const std::bitset<32> data_bits{ data };
|
||||
for (size_t i = 0; i < data_bits.size(); ++i)
|
||||
{
|
||||
if (data_bits.test(i))
|
||||
{
|
||||
m_state.append_button((unsigned int)i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
175
src/slic3r/GUI/Mouse3DController.hpp
Normal file
175
src/slic3r/GUI/Mouse3DController.hpp
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
#ifndef slic3r_Mouse3DController_hpp_
|
||||
#define slic3r_Mouse3DController_hpp_
|
||||
|
||||
// Enabled debug output to console and extended imgui dialog
|
||||
#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
struct Camera;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
// 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) {}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
// The following variable is used to count the potential mouse wheel events triggered and is updated by:
|
||||
// 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
|
||||
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
|
||||
|
||||
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(); }
|
||||
|
||||
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_translation_deadzone() const { return m_translation_params.deadzone; }
|
||||
void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; }
|
||||
|
||||
float get_rotation_deadzone() const { return m_rotation_params.deadzone; }
|
||||
void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; }
|
||||
|
||||
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||
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; }
|
||||
#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);
|
||||
};
|
||||
|
||||
bool m_initialized;
|
||||
mutable State m_state;
|
||||
std::mutex m_mutex;
|
||||
std::thread m_thread;
|
||||
hid_device* m_device;
|
||||
std::string m_device_str;
|
||||
bool m_running;
|
||||
bool m_settings_dialog;
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_last_time;
|
||||
|
||||
public:
|
||||
Mouse3DController();
|
||||
|
||||
void init();
|
||||
void shutdown();
|
||||
|
||||
bool is_device_connected() const { return m_device != nullptr; }
|
||||
bool is_running() const { return m_running; }
|
||||
|
||||
bool process_mouse_wheel() { std::lock_guard<std::mutex> lock(m_mutex); return m_state.process_mouse_wheel(); }
|
||||
|
||||
bool apply(Camera& camera);
|
||||
|
||||
bool is_settings_dialog_shown() const { return m_settings_dialog; }
|
||||
void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); }
|
||||
void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const;
|
||||
|
||||
private:
|
||||
bool connect_device();
|
||||
void disconnect_device();
|
||||
void start();
|
||||
void stop() { m_running = false; }
|
||||
|
||||
// secondary thread methods
|
||||
void run();
|
||||
void collect_input();
|
||||
|
||||
typedef std::array<unsigned char, 13> DataPacket;
|
||||
bool handle_packet(const DataPacket& packet);
|
||||
bool handle_wireless_packet(const DataPacket& packet);
|
||||
bool handle_packet_translation(const DataPacket& packet);
|
||||
bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte);
|
||||
bool handle_packet_button(const DataPacket& packet, unsigned int packet_size);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_Mouse3DController_hpp_
|
||||
|
||||
|
|
@ -65,6 +65,7 @@
|
|||
#include "GUI_Preview.hpp"
|
||||
#include "3DBed.hpp"
|
||||
#include "Camera.hpp"
|
||||
#include "Mouse3DController.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
#include "BackgroundSlicingProcess.hpp"
|
||||
|
|
@ -1387,9 +1388,6 @@ struct Plater::priv
|
|||
Slic3r::Model model;
|
||||
PrinterTechnology printer_technology = ptFFF;
|
||||
Slic3r::GCodePreviewData gcode_preview_data;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
std::vector<Slic3r::ThumbnailData> thumbnail_data;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
// GUI elements
|
||||
wxSizer* panel_sizer{ nullptr };
|
||||
|
|
@ -1398,6 +1396,7 @@ struct Plater::priv
|
|||
Sidebar *sidebar;
|
||||
Bed3D bed;
|
||||
Camera camera;
|
||||
Mouse3DController mouse3d_controller;
|
||||
View3D* view3D;
|
||||
GLToolbar view_toolbar;
|
||||
Preview *preview;
|
||||
|
|
@ -1946,6 +1945,7 @@ struct Plater::priv
|
|||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool transparent_background);
|
||||
void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background);
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
void msw_rescale_object_menu();
|
||||
|
|
@ -2016,7 +2016,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
background_process.set_sla_print(&sla_print);
|
||||
background_process.set_gcode_preview_data(&gcode_preview_data);
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
background_process.set_thumbnail_data(&thumbnail_data);
|
||||
background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background)
|
||||
{
|
||||
std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool)> task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) {
|
||||
generate_thumbnails(thumbnails, sizes, printable_only, parts_only, transparent_background);
|
||||
});
|
||||
std::future<void> result = task.get_future();
|
||||
wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, transparent_background); });
|
||||
result.wait();
|
||||
});
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
|
||||
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
|
||||
|
|
@ -2087,6 +2095,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
|
||||
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
|
||||
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||
|
||||
// 3DScene/Toolbar:
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
|
||||
|
|
@ -2136,12 +2149,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
// updates camera type from .ini file
|
||||
camera.set_type(get_config("use_perspective_camera"));
|
||||
|
||||
mouse3d_controller.init();
|
||||
|
||||
// Initialize the Undo / Redo stack with a first snapshot.
|
||||
this->take_snapshot(_(L("New Project")));
|
||||
}
|
||||
|
||||
Plater::priv::~priv()
|
||||
{
|
||||
mouse3d_controller.shutdown();
|
||||
|
||||
if (config != nullptr)
|
||||
delete config;
|
||||
}
|
||||
|
|
@ -3062,37 +3079,6 @@ bool Plater::priv::restart_background_process(unsigned int state)
|
|||
( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) ||
|
||||
(state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 ||
|
||||
(state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) {
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
if (((state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) == 0) &&
|
||||
(this->background_process.state() != BackgroundSlicingProcess::STATE_RUNNING))
|
||||
{
|
||||
// update thumbnail data
|
||||
const std::vector<Vec2d> &thumbnail_sizes = this->background_process.current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values;
|
||||
if (this->printer_technology == ptFFF)
|
||||
{
|
||||
// for ptFFF we need to generate the thumbnails before the export of gcode starts
|
||||
this->thumbnail_data.clear();
|
||||
for (const Vec2d &sized : thumbnail_sizes)
|
||||
{
|
||||
this->thumbnail_data.push_back(ThumbnailData());
|
||||
Point size(sized); // round to ints
|
||||
generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false);
|
||||
}
|
||||
}
|
||||
else if (this->printer_technology == ptSLA)
|
||||
{
|
||||
// for ptSLA generate thumbnails without supports and pad (not yet calculated)
|
||||
// to render also supports and pad see on_slicing_update()
|
||||
this->thumbnail_data.clear();
|
||||
for (const Vec2d &sized : thumbnail_sizes)
|
||||
{
|
||||
this->thumbnail_data.push_back(ThumbnailData());
|
||||
Point size(sized); // round to ints
|
||||
generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
// The print is valid and it can be started.
|
||||
if (this->background_process.start()) {
|
||||
this->statusbar()->set_cancel_callback([this]() {
|
||||
|
|
@ -3339,6 +3325,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
|||
} else
|
||||
view3D->reload_scene(true);
|
||||
}
|
||||
|
||||
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
|
||||
view3D->set_as_dirty();
|
||||
view_toolbar.select_item("3D");
|
||||
|
|
@ -3353,6 +3340,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
|||
this->q->reslice();
|
||||
// keeps current gcode preview, if any
|
||||
preview->reload_print(true);
|
||||
|
||||
preview->set_canvas_as_dirty();
|
||||
view_toolbar.select_item("Preview");
|
||||
}
|
||||
|
|
@ -3430,25 +3418,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
|||
} else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
|
||||
// Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
|
||||
this->preview->reload_print();
|
||||
|
||||
// uncomment the following lines if you want to render into the thumbnail also supports and pad for SLA printer
|
||||
/*
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
// update thumbnail data
|
||||
// for ptSLA generate the thumbnail after supports and pad have been calculated to have them rendered
|
||||
if ((this->printer_technology == ptSLA) && (evt.status.percent == -3))
|
||||
{
|
||||
const std::vector<Vec2d>& thumbnail_sizes = this->background_process.current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values;
|
||||
this->thumbnail_data.clear();
|
||||
for (const Vec2d &sized : thumbnail_sizes)
|
||||
{
|
||||
this->thumbnail_data.push_back(ThumbnailData());
|
||||
Point size(sized); // round to ints
|
||||
generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, false, false);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3679,6 +3648,19 @@ void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsig
|
|||
{
|
||||
view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, transparent_background);
|
||||
}
|
||||
|
||||
void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background)
|
||||
{
|
||||
thumbnails.clear();
|
||||
for (const Vec2d& size : sizes)
|
||||
{
|
||||
thumbnails.push_back(ThumbnailData());
|
||||
Point isize(size); // round to ints
|
||||
generate_thumbnail(thumbnails.back(), isize.x(), isize.y(), printable_only, parts_only, transparent_background);
|
||||
if (!thumbnails.back().is_valid())
|
||||
thumbnails.pop_back();
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
|
||||
void Plater::priv::msw_rescale_object_menu()
|
||||
|
|
@ -5241,6 +5223,16 @@ const Camera& Plater::get_camera() const
|
|||
return p->camera;
|
||||
}
|
||||
|
||||
const Mouse3DController& Plater::get_mouse3d_controller() const
|
||||
{
|
||||
return p->mouse3d_controller;
|
||||
}
|
||||
|
||||
Mouse3DController& Plater::get_mouse3d_controller()
|
||||
{
|
||||
return p->mouse3d_controller;
|
||||
}
|
||||
|
||||
bool Plater::can_delete() const { return p->can_delete(); }
|
||||
bool Plater::can_delete_all() const { return p->can_delete_all(); }
|
||||
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class ObjectSettings;
|
|||
class ObjectLayers;
|
||||
class ObjectList;
|
||||
class GLCanvas3D;
|
||||
class Mouse3DController;
|
||||
|
||||
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
|
||||
|
||||
|
|
@ -260,6 +261,8 @@ public:
|
|||
void msw_rescale();
|
||||
|
||||
const Camera& get_camera() const;
|
||||
const Mouse3DController& get_mouse3d_controller() const;
|
||||
Mouse3DController& get_mouse3d_controller();
|
||||
|
||||
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
|
||||
class SuppressSnapshots
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue