diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8ba743e102..cd75bdfc59 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3508,6 +3508,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) wxGetApp().obj_list()->update_selections(); return; } else if (hover_volume->emboss_shape.has_value()) { + m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id}); if (type != GLGizmosManager::EType::Svg) m_gizmos.open_gizmo(GLGizmosManager::EType::Svg); wxGetApp().obj_list()->update_selections(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 950e257998..67fe1e8449 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -62,7 +62,6 @@ #define SHOW_FINE_POSITION // draw convex hull around volume #define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position #define ALLOW_OPEN_NEAR_VOLUME -#define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread #endif // ALLOW_DEBUG_MODE //#define USE_PIXEL_SIZE_IN_WX_FONT @@ -797,16 +796,10 @@ void GLGizmoEmboss::on_set_state() // change position of just opened emboss window if (m_allow_open_near_volume) { m_set_window_offset = calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); - } else { - if (m_gui_cfg != nullptr) - m_set_window_offset = ImGuiWrapper::change_window_position(on_get_name().c_str(), false); - else - m_set_window_offset = ImVec2(-1, -1); + } else { + m_set_window_offset = (m_gui_cfg != nullptr) ? + ImGuiWrapper::change_window_position(on_get_name().c_str(), false) : ImVec2(-1, -1); } - - // when open by hyperlink it needs to show up - // or after key 'T' windows doesn't appear - m_parent.set_as_dirty(); } } @@ -1110,29 +1103,6 @@ void GLGizmoEmboss::calculate_scale() { m_style_manager.clear_imgui_font(); } -#ifdef EXECUTE_PROCESS_ON_MAIN_THREAD -namespace priv { -// Run Job on main thread (blocking) - ONLY DEBUG -static inline void execute_job(std::shared_ptr j) -{ - struct MyCtl : public Job::Ctl - { - void update_status(int st, const std::string &msg = "") override{}; - bool was_canceled() const override { return false; } - std::future call_on_main_thread(std::function fn) override - { - return std::future{}; - } - } ctl; - j->process(ctl); - wxGetApp().plater()->CallAfter([j]() { - std::exception_ptr e_ptr = nullptr; - j->finalize(false, e_ptr); - }); -} -} // namespace priv -#endif - bool GLGizmoEmboss::process() { // no volume is selected -> selection from right panel @@ -1151,50 +1121,12 @@ bool GLGizmoEmboss::process() if (!m_volume->emboss_shape.has_value()) return false; DataUpdate data{create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()}; - std::unique_ptr job = nullptr; + bool start = start_update_volume(std::move(data), *m_volume, m_parent.get_selection(), m_raycast_manager); + if (start) + // notification is removed befor object is changed by job + remove_notification_not_valid_font(); - // check cutting from source mesh - bool &use_surface = data.base->shape.projection.use_surface; - if (use_surface && m_volume->is_the_only_one_part()) - use_surface = false; - - if (use_surface) { - // Model to cut surface from. - SurfaceVolumeData::ModelSources sources = create_volume_sources(*m_volume); - if (sources.empty()) return false; - - Transform3d text_tr = m_volume->get_matrix(); - auto& fix_3mf = m_volume->emboss_shape->fix_3mf_tr; - if (fix_3mf.has_value()) - text_tr = text_tr * fix_3mf->inverse(); - - // when it is new applying of use surface than move origin onto surfaca - if (!m_volume->emboss_shape->projection.use_surface) { - auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager); - if (offset.has_value()) - text_tr *= Eigen::Translation(*offset); - } - - bool is_outside = m_volume->is_model_part(); - // check that there is not unexpected volume type - assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier()); - UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}}; - job = std::make_unique(std::move(surface_data)); - } else { - job = std::make_unique(std::move(data)); - } - -#ifndef EXECUTE_PROCESS_ON_MAIN_THREAD - auto &worker = wxGetApp().plater()->get_ui_job_worker(); - queue_job(worker, std::move(job)); -#else - // Run Job on main thread (blocking) - ONLY DEBUG - priv::execute_job(std::move(job)); -#endif // EXECUTE_PROCESS_ON_MAIN_THREAD - - // notification is removed befor object is changed by job - remove_notification_not_valid_font(); - return true; + return start; } namespace { @@ -2475,38 +2407,6 @@ bool GLGizmoEmboss::rev_slider(const std::string &name, undo_tooltip, undo_offset, draw_slider_float); } -void GLGizmoEmboss::do_translate(const Vec3d &relative_move) -{ - assert(m_volume != nullptr); - assert(m_volume->text_configuration.has_value()); - Selection &selection = m_parent.get_selection(); - assert(!selection.is_empty()); - selection.setup_cache(); - selection.translate(relative_move, TransformationType::Local); - - std::string snapshot_name; // empty mean no store undo / redo - // NOTE: it use L instead of _L macro because prefix _ is appended inside - // function do_move - // snapshot_name = L("Set surface distance"); - m_parent.do_move(snapshot_name); -} - -void GLGizmoEmboss::do_rotate(float relative_z_angle) -{ - assert(m_volume != nullptr); - assert(m_volume->text_configuration.has_value()); - Selection &selection = m_parent.get_selection(); - assert(!selection.is_empty()); - selection.setup_cache(); - selection.rotate(Vec3d(0., 0., relative_z_angle), get_transformation_type(selection)); - - std::string snapshot_name; // empty meand no store undo / redo - // NOTE: it use L instead of _L macro because prefix _ is appended - // inside function do_move - // snapshot_name = L("Set text rotation"); - m_parent.do_rotate(snapshot_name); -} - void GLGizmoEmboss::draw_advanced() { const auto &ff = m_style_manager.get_font_file_with_cache(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp index 9f56437885..8727391968 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -35,15 +35,9 @@ using namespace Slic3r::Emboss; using namespace Slic3r::GUI; using namespace Slic3r::GUI::Emboss; -namespace priv { - -} // namespace priv - GLGizmoSVG::GLGizmoSVG(GLCanvas3D &parent) : GLGizmoBase(parent, M_ICON_FILENAME, -3) - , m_volume(nullptr) , m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z) // grab id = 2 (Z axis) - , m_job_cancel(nullptr) { m_rotate_gizmo.set_group_id(0); m_rotate_gizmo.set_force_local_coordinate(true); @@ -111,8 +105,39 @@ std::string volume_name(const EmbossShape& shape); /// Type of volume to be created /// Params CreateVolumeParams create_input(GLCanvas3D &canvas, RaycastManager &raycaster, ModelVolumeType volume_type); + +// This configs holds GUI layout size given by translated texts. +// etc. When language changes, GUI is recreated and this class constructed again, +// so the change takes effect. (info by GLGizmoFdmSupports.hpp) +struct GuiCfg +{ + // Detect invalid config values when change monitor DPI + double screen_scale; + float main_toolbar_height; + + // Zero means it is calculated in init function + ImVec2 minimal_window_size = ImVec2(0, 0); + + float input_width = 0.f; + float input_offset = 0.f; + + float icon_width = 0.f; + // Only translations needed for calc GUI size + struct Translations + { + std::string depth; + std::string use_surface; + std::string rotation; + std::string distance; // from surface + }; + Translations translations; +}; +GuiCfg create_gui_configuration(); + } // namespace +// use private definition +struct GLGizmoSVG::GuiCfg: public ::GuiCfg{}; bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos) { @@ -150,9 +175,8 @@ bool GLGizmoSVG::on_mouse_for_rotation(const wxMouseEvent &mouse_event) if (!m_dragging) return used; if (mouse_event.Dragging()) { - auto &angle_opt = m_volume->text_configuration->style.prop.angle; if (!m_rotate_start_angle.has_value()) - m_rotate_start_angle = angle_opt.has_value() ? *angle_opt : 0.f; + m_rotate_start_angle = m_angle.value_or(0.f); double angle = m_rotate_gizmo.get_angle(); angle -= PI / 2; // Grabber is upward @@ -165,11 +189,11 @@ bool GLGizmoSVG::on_mouse_for_rotation(const wxMouseEvent &mouse_event) // move to range <-M_PI, M_PI> Geometry::to_range_pi_pi(angle); // propagate angle into property - angle_opt = static_cast(angle); + m_angle = static_cast(angle); // do not store zero - if (is_approx(*angle_opt, 0.f)) - angle_opt.reset(); + if (is_approx(*m_angle, 0.f)) + m_angle.reset(); } return used; } @@ -192,7 +216,7 @@ bool GLGizmoSVG::on_mouse_for_translate(const wxMouseEvent &mouse_event) // End with surface dragging? if (was_dragging && !is_dragging) { // Update surface by new position - if (m_volume->text_configuration->style.prop.use_surface) + if (m_volume->emboss_shape->projection.use_surface) process(); // Show correct value of height & depth inside of inputs @@ -226,7 +250,7 @@ bool GLGizmoSVG::on_mouse(const wxMouseEvent &mouse_event) // not selected volume if (m_volume == nullptr || get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || - !m_volume->text_configuration.has_value()) return false; + !m_volume->emboss_shape.has_value()) return false; if (on_mouse_for_rotation(mouse_event)) return true; if (on_mouse_for_translate(mouse_event)) return true; @@ -244,10 +268,6 @@ bool GLGizmoSVG::on_init() m_rotate_gizmo.init(); ColorRGBA gray_color(.6f, .6f, .6f, .3f); m_rotate_gizmo.set_highlight_color(gray_color); - - // No shortCut - // m_shortcut_key = WXK_CONTROL_T; - // Set rotation gizmo upwardrotate m_rotate_gizmo.set_angle(PI / 2); return true; @@ -292,15 +312,18 @@ void GLGizmoSVG::on_render_input_window(float x, float y, float bottom_limit) // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); - if (!m_gui_cfg.has_value() || // Exist configuration - first run + if (m_gui_cfg == nullptr || // Exist configuration - first run m_gui_cfg->screen_scale != screen_scale || // change of DPI m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port ) { // Create cache for gui offsets - GuiCfg cfg = create_gui_configuration(); + ::GuiCfg cfg = create_gui_configuration(); cfg.screen_scale = screen_scale; cfg.main_toolbar_height = main_toolbar_height; - m_gui_cfg.emplace(std::move(cfg)); + + GuiCfg gui_cfg{std::move(cfg)}; + m_gui_cfg = std::make_unique(std::move(gui_cfg)); + // set position near toolbar m_set_window_offset = ImVec2(-1.f, -1.f); } @@ -358,29 +381,13 @@ void GLGizmoSVG::on_set_state() // Closing gizmo. e.g. selecting another one if (GLGizmoBase::m_state == GLGizmoBase::Off) { - // refuse outgoing during text preview - if (false) { - GLGizmoBase::m_state = GLGizmoBase::On; - auto notification_manager = wxGetApp().plater()->get_notification_manager(); - notification_manager->push_notification( - NotificationType::CustomNotification, - NotificationManager::NotificationLevel::RegularNotificationLevel, - _u8L("ERROR: Wait until ends or Cancel process.")); - return; - } reset_volume(); } else if (GLGizmoBase::m_state == GLGizmoBase::On) { // Try(when exist) set text configuration by volume set_volume_by_selection(); - - if (!m_gui_cfg.has_value()) - m_set_window_offset = ImGuiWrapper::change_window_position(on_get_name().c_str(), false); - else - m_set_window_offset = ImVec2(-1, -1); - - // when open by hyperlink it needs to show up - // or after key 'T' windows doesn't appear - // m_parent.set_as_dirty(); + + m_set_window_offset = (m_gui_cfg != nullptr) ? + ImGuiWrapper::change_window_position(on_get_name().c_str(), false) : ImVec2(-1, -1); } } @@ -413,14 +420,6 @@ void GLGizmoSVG::on_stop_dragging() } void GLGizmoSVG::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); } -GLGizmoSVG::GuiCfg GLGizmoSVG::create_gui_configuration() -{ - GuiCfg cfg; // initialize by default values; - - - return cfg; -} - void GLGizmoSVG::set_volume_by_selection() { const Selection &selection = m_parent.get_selection(); @@ -454,6 +453,7 @@ void GLGizmoSVG::set_volume_by_selection() m_volume = volume; m_volume_id = volume->id(); + m_volume_shape = *volume->emboss_shape; // copy // Calculate current angle of up vector m_angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); @@ -469,6 +469,7 @@ void GLGizmoSVG::reset_volume() m_volume = nullptr; m_volume_id.id = 0; + m_volume_shape.shapes.clear(); } void GLGizmoSVG::calculate_scale() { @@ -496,51 +497,25 @@ bool GLGizmoSVG::process() { // no volume is selected -> selection from right panel assert(m_volume != nullptr); - if (m_volume == nullptr) return false; - - //DataUpdate data{create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()}; + if (m_volume == nullptr) + return false; + + assert(m_volume->emboss_shape.has_value()); + if (!m_volume->emboss_shape.has_value()) + return false; - //std::unique_ptr job = nullptr; + // Cancel previous Job, when it is in process + // worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs + // Cancel only EmbossUpdateJob no others + if (m_job_cancel != nullptr) + m_job_cancel->store(true); + // create new shared ptr to cancel new job + m_job_cancel = std::make_shared>(false); - //// check cutting from source mesh - //bool &use_surface = data.text_configuration.style.prop.use_surface; - //bool is_object = m_volume->get_object()->volumes.size() == 1; - //if (use_surface && is_object) - // use_surface = false; - // - //if (use_surface) { - // // Model to cut surface from. - // SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume); - // if (sources.empty()) return false; - - // Transform3d text_tr = m_volume->get_matrix(); - // auto& fix_3mf = m_volume->text_configuration->fix_3mf_tr; - // if (fix_3mf.has_value()) - // text_tr = text_tr * fix_3mf->inverse(); - - // // when it is new applying of use surface than move origin onto surfaca - // if (!m_volume->text_configuration->style.prop.use_surface) { - // auto offset = calc_surface_offset(*m_volume, m_raycast_manager, m_parent.get_selection()); - // if (offset.has_value()) - // text_tr *= Eigen::Translation(*offset); - // } - - // bool is_outside = m_volume->is_model_part(); - // // check that there is not unexpected volume type - // assert(is_outside || m_volume->is_negative_volume() || - // m_volume->is_modifier()); - // UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}}; - // job = std::make_unique(std::move(surface_data)); - //} else { - // job = std::make_unique(std::move(data)); - //} - - //auto &worker = wxGetApp().plater()->get_ui_job_worker(); - //queue_job(worker, std::move(job)); - - //// notification is removed befor object is changed by job - //remove_notification_not_valid_font(); - return true; + EmbossShape shape = m_volume_shape; // copy + auto base = std::make_unique(m_volume->name, m_job_cancel, std::move(shape)); + DataUpdate data{std::move(base), m_volume_id}; + return start_update_volume(std::move(data), *m_volume, m_parent.get_selection(), m_raycast_manager); } void GLGizmoSVG::close() @@ -555,18 +530,54 @@ void GLGizmoSVG::draw_window() { if (m_volume != nullptr && m_volume->emboss_shape.has_value()) ImGui::Text("SVG file path is %s", m_volume->emboss_shape->svg_file_path.c_str()); - //draw_use_surface(); + + ImGui::Indent(m_gui_cfg->icon_width); + draw_depth(); + draw_use_surface(); draw_distance(); draw_rotation(); + ImGui::Unindent(m_gui_cfg->icon_width); if (ImGui::Button("change file")) { auto data = create_emboss_data_base(m_job_cancel); - std::string file = choose_svg_file(); + std::string file = ::choose_svg_file(); } ImGui::Separator(); draw_model_type(); - } +} + +void GLGizmoSVG::draw_depth() +{ + ImGuiWrapper::text(m_gui_cfg->translations.depth); + ImGui::SameLine(m_gui_cfg->input_offset); + ImGui::SetNextItemWidth(m_gui_cfg->input_width); + + bool use_inch = wxGetApp().app_config->get_bool("use_inches"); + double &value = m_volume_shape.projection.depth; + const wxString tooltip = _L("Size in emboss direction."); + if (use_inch) { + const char *size_format = "%.2f in"; + double value_inch = value * ObjectManipulation::mm_to_in; + if (ImGui::InputDouble("##depth", &value_inch, 1., 10., size_format)) { + value = value_inch * ObjectManipulation::in_to_mm; + process(); + } + } else { + const char *size_format = "%.1f mm"; + if (ImGui::InputDouble("##depth", &value, 1., 10., size_format)) + process(); + } +} + +void GLGizmoSVG::draw_use_surface() +{ + ImGuiWrapper::text(m_gui_cfg->translations.use_surface); + ImGui::SameLine(m_gui_cfg->input_offset); + + if (ImGui::Checkbox("##useSurface", &m_volume_shape.projection.use_surface)) + process(); +} void GLGizmoSVG::draw_distance() { @@ -584,7 +595,10 @@ void GLGizmoSVG::draw_distance() m_imgui->disabled_begin(!allowe_surface_distance); ScopeGuard sg([imgui = m_imgui]() { imgui->disabled_end(); }); - ImGuiWrapper::text(_L("distance")); + ImGuiWrapper::text(m_gui_cfg->translations.distance); + ImGui::SameLine(m_gui_cfg->input_offset); + ImGui::SetNextItemWidth(m_gui_cfg->input_width); + bool use_inch = wxGetApp().app_config->get_bool("use_inches"); const wxString move_tooltip = _L("Distance of the center of the text to the model surface."); bool is_moved = false; @@ -606,13 +620,16 @@ void GLGizmoSVG::draw_distance() is_moved = true; } - float undo_offset = ImGui::GetStyle().FramePadding.x; - ImGui::SameLine(undo_offset); + m_imgui->disabled_begin(!m_distance.has_value() && allowe_surface_distance); + ScopeGuard sg2([imgui = m_imgui]() { imgui->disabled_end(); }); + + float reset_offset = ImGui::GetStyle().FramePadding.x; + ImGui::SameLine(reset_offset); if (ImGui::Button("R##distance_reset")){ m_distance.reset(); is_moved = true; } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Reset distance to zero value")); + ImGui::SetTooltip("%s", _u8L("Reset distance to zero value").c_str()); if (is_moved) do_local_z_move(m_parent, m_distance.value_or(.0f) - prev_distance); @@ -622,13 +639,18 @@ void GLGizmoSVG::draw_rotation() { if (m_volume == nullptr) return; + + ImGuiWrapper::text(m_gui_cfg->translations.rotation); + ImGui::SameLine(m_gui_cfg->input_offset); + ImGui::SetNextItemWidth(m_gui_cfg->input_width); + // slider for Clock-wise angle in degress // stored angle is optional CCW and in radians // Convert stored value to degress // minus create clock-wise roation from CCW float angle = m_angle.value_or(0.f); float angle_deg = static_cast(-angle * 180 / M_PI); - if (m_imgui->slider_float("##angle", &angle, limits.angle.min, limits.angle.max, u8"%.2f DEG", 1.f, false, _L("Rotate text Clock-wise."))){ + if (m_imgui->slider_float("##angle", &angle_deg, limits.angle.min, limits.angle.max, u8"%.2f DEG", 1.f, false, _L("Rotate text Clock-wise."))){ // convert back to radians and CCW double angle_rad = -angle_deg * M_PI / 180.0; Geometry::to_range_pi_pi(angle_rad); @@ -645,6 +667,22 @@ void GLGizmoSVG::draw_rotation() process(); } + // Reset button + m_imgui->disabled_begin(!m_angle.has_value()); + ScopeGuard sg([imgui = m_imgui]() { imgui->disabled_end(); }); + + float reset_offset = ImGui::GetStyle().FramePadding.x; + ImGui::SameLine(reset_offset); + if (ImGui::Button("R##angle_reset")) { + do_local_z_rotate(m_parent, -(*m_angle)); + m_angle.reset(); + + // recalculate for surface cut + if (m_volume->emboss_shape->projection.use_surface) + process(); + } else if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", _u8L("Reset rotation to zero value").c_str()); + // Keep up - lock button icon //ImGui::SameLine(m_gui_cfg->lock_offset); //const IconManager::Icon &icon = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::activable); @@ -788,6 +826,39 @@ CreateVolumeParams create_input(GLCanvas3D &canvas, RaycastManager& raycaster, M plater->get_ui_job_worker(), volume_type, raycaster, gizmo, gl_volume}; } +GuiCfg create_gui_configuration() { + GuiCfg cfg; // initialize by default values; + + float line_height = ImGui::GetTextLineHeight(); + float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); + + float space = line_height_with_spacing - line_height; + + cfg.icon_width = line_height; + + GuiCfg::Translations &tr = cfg.translations; + + tr.depth = _u8L("Depth"); + tr.use_surface = _u8L("Use surface"); + tr.distance = _u8L("From surface"); + tr.rotation = _u8L("Rotation"); + float max_tr_width = std::max({ + ImGui::CalcTextSize(tr.depth.c_str()).x, + ImGui::CalcTextSize(tr.use_surface.c_str()).x, + ImGui::CalcTextSize(tr.distance.c_str()).x, + ImGui::CalcTextSize(tr.rotation.c_str()).x, + }); + + const ImGuiStyle &style = ImGui::GetStyle(); + cfg.input_offset = style.WindowPadding.x + max_tr_width + space + cfg.icon_width; + + ImVec2 letter_m_size = ImGui::CalcTextSize("M"); + const float count_letter_M_in_input = 12.f; + cfg.input_width = letter_m_size.x * count_letter_M_in_input; + + return cfg; +} + std::string choose_svg_file() { wxWindow *parent = nullptr; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp index dbd80e6192..efb5ec9904 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp @@ -98,6 +98,8 @@ private: bool process(); void close(); void draw_window(); + void draw_depth(); + void draw_use_surface(); void draw_distance(); void draw_rotation(); void draw_model_type(); @@ -105,41 +107,20 @@ private: // process mouse event bool on_mouse_for_rotation(const wxMouseEvent &mouse_event); bool on_mouse_for_translate(const wxMouseEvent &mouse_event); - - // This configs holds GUI layout size given by translated texts. - // etc. When language changes, GUI is recreated and this class constructed again, - // so the change takes effect. (info by GLGizmoFdmSupports.hpp) - struct GuiCfg - { - // Detect invalid config values when change monitor DPI - double screen_scale; - float main_toolbar_height; - - // Zero means it is calculated in init function - ImVec2 minimal_window_size = ImVec2(0, 0); - - float input_width = 0.f; - float input_offset = 0.f; - - // Only translations needed for calc GUI size - struct Translations - { - std::string font; - }; - Translations translations; - }; - std::optional m_gui_cfg; - static GuiCfg create_gui_configuration(); + + struct GuiCfg; + std::unique_ptr m_gui_cfg = nullptr; // actual selected only one volume - with emboss data - ModelVolume *m_volume; + ModelVolume *m_volume = nullptr; + EmbossShape m_volume_shape; // copy from m_volume for edit // When work with undo redo stack there could be situation that // m_volume point to unexisting volume so One need also objectID ObjectID m_volume_id; // cancel for previous update of volume to cancel finalize part - std::shared_ptr> m_job_cancel; + std::shared_ptr> m_job_cancel = nullptr; // Rotation gizmo GLGizmoRotate m_rotate_gizmo; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 43f3e2500f..0a840b1d4a 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -23,6 +23,8 @@ #include "slic3r/Utils/UndoRedo.hpp" #include "slic3r/Utils/RaycastManager.hpp" +// #define EXECUTE_UPDATE_ON_MAIN_THREAD // debug execution on main thread + using namespace Slic3r; using namespace Slic3r::Emboss; using namespace Slic3r::GUI; @@ -607,6 +609,74 @@ bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr bool try_no_coor = false; return ::start_create_volume_on_surface_job(input, std::move(data), coor, try_no_coor); } + +#ifdef EXECUTE_UPDATE_ON_MAIN_THREAD +namespace { +// Run Job on main thread (blocking) - ONLY DEBUG +static inline bool execute_job(std::shared_ptr j) +{ + struct MyCtl : public Job::Ctl + { + void update_status(int st, const std::string &msg = "") override{}; + bool was_canceled() const override { return false; } + std::future call_on_main_thread(std::function fn) override { return std::future{}; } + } ctl; + j->process(ctl); + wxGetApp().plater()->CallAfter([j]() { + std::exception_ptr e_ptr = nullptr; + j->finalize(false, e_ptr); + }); + return true; +} +} // namespace +#endif + +bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Selection &selection, RaycastManager& raycaster) +{ + assert(data.volume_id == volume.id()); + + // check cutting from source mesh + bool &use_surface = data.base->shape.projection.use_surface; + if (use_surface && volume.is_the_only_one_part()) + use_surface = false; + + std::unique_ptr job = nullptr; + if (use_surface) { + // Model to cut surface from. + SurfaceVolumeData::ModelSources sources = create_volume_sources(volume); + if (sources.empty()) + return false; + + Transform3d volume_tr = volume.get_matrix(); + const std::optional &fix_3mf = volume.emboss_shape->fix_3mf_tr; + if (fix_3mf.has_value()) + volume_tr = volume_tr * fix_3mf->inverse(); + + // when it is new applying of use surface than move origin onto surfaca + if (!volume.emboss_shape->projection.use_surface) { + auto offset = calc_surface_offset(selection, raycaster); + if (offset.has_value()) + volume_tr *= Eigen::Translation(*offset); + } + + bool is_outside = volume.is_model_part(); + // check that there is not unexpected volume type + assert(is_outside || volume.is_negative_volume() || volume.is_modifier()); + UpdateSurfaceVolumeData surface_data{std::move(data), {volume_tr, is_outside, std::move(sources)}}; + job = std::make_unique(std::move(surface_data)); + } else { + job = std::make_unique(std::move(data)); + } + +#ifndef EXECUTE_UPDATE_ON_MAIN_THREAD + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + return queue_job(worker, std::move(job)); +#else + // Run Job on main thread (blocking) - ONLY DEBUG + return execute_job(std::move(job)); +#endif // EXECUTE_UPDATE_ON_MAIN_THREAD +} + } // namespace Slic3r::GUI::Emboss //////////////////////////// diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index 7a3a39b7b7..eab04a5696 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -25,6 +25,7 @@ class RaycastManager; class Plater; class GLCanvas3D; class Worker; +class Selection; }} namespace Slic3r::GUI::Emboss { @@ -217,6 +218,17 @@ bool start_create_volume(CreateVolumeParams &input, DataBasePtr data, const Vec2 /// Need to suggest position or put near the selection /// bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr data); + +/// +/// Start job for update embossed volume +/// +/// define update data +/// Volume to be updated +/// Keep model and gl_volumes - when start use surface volume must be selected +/// Could cast ray to scene +/// True when start job otherwise false +bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Selection &selection, RaycastManager &raycaster); + } // namespace Slic3r::GUI #endif // slic3r_EmbossJob_hpp_ diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 9c522cb372..07c837ca0c 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -310,7 +310,7 @@ std::optional calc_surface_offset(const Selection &selection, RaycastMana auto cond = RaycastManager::SkipVolume(volume->id().id); raycast_manager.actualize(*instance, &cond); - Transform3d to_world = world_matrix_fixed(gl_volume, selection.get_model()->objects); + Transform3d to_world = world_matrix_fixed(gl_volume, objects); Vec3d point = to_world * Vec3d::Zero(); Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); @@ -491,8 +491,12 @@ void do_local_z_rotate(GLCanvas3D &canvas, double relative_angle) assert(!selection.is_empty()); if(selection.is_empty()) return; + assert(selection.is_single_full_object() || selection.is_single_volume()); + if (!selection.is_single_full_object() && !selection.is_single_volume()) return; + selection.setup_cache(); - TransformationType transformation_type = TransformationType::Local_Relative_Joint; + TransformationType transformation_type = selection.is_single_volume() ? + TransformationType::Local_Relative_Joint : TransformationType::Instance_Relative_Joint; selection.rotate(Vec3d(0., 0., relative_angle), transformation_type); std::string snapshot_name; // empty meand no store undo / redo