diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d5ee6a0484..81880831f1 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -169,6 +169,9 @@ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2 std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const { + if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) + throw std::invalid_argument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); + std::string gcode; // Toolchangeresult.gcode assumes the wipe tower corner is at the origin @@ -182,8 +185,11 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T end_pos = Eigen::Rotation2Df(alpha) * end_pos; end_pos += m_wipe_tower_pos; } - std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha); - + + Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; + float wipe_tower_rotation = tcr.priming ? 0.f : alpha; + + std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); // Disable linear advance for the wipe tower operations. gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n")); @@ -285,17 +291,21 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) -std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const +std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const { - std::istringstream gcode_str(gcode_original); + Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); + + std::istringstream gcode_str(tcr.gcode); std::string gcode_out; std::string line; - Vec2f pos = start_pos; - Vec2f transformed_pos; + Vec2f pos = tcr.start_pos; + Vec2f transformed_pos = pos; Vec2f old_pos(-1000.1f, -1000.1f); while (gcode_str) { std::getline(gcode_str, line); // we read the gcode line by line + + // All G1 commands should be translated and rotated if (line.find("G1 ") == 0) { std::ostringstream line_out; std::istringstream line_str(line); @@ -317,17 +327,34 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco if (transformed_pos != old_pos) { line = line_out.str(); - char buf[2048] = "G1"; + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) << "G1 "; if (transformed_pos.x() != old_pos.x()) - sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x()); + oss << " X" << transformed_pos.x() - extruder_offset.x(); if (transformed_pos.y() != old_pos.y()) - sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y()); + oss << " Y" << transformed_pos.y() - extruder_offset.y(); - line.replace(line.find("G1 "), 3, buf); + line.replace(line.find("G1 "), 3, oss.str()); old_pos = transformed_pos; } } + gcode_out += line + "\n"; + + // If this was a toolchange command, we should change current extruder offset + if (line == "[toolchange_gcode]") { + extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); + + // If the extruder offset changed, add an extra move so everything is continuous + if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) + << "G1 X" << transformed_pos.x() - extruder_offset.x() + << " Y" << transformed_pos.y() - extruder_offset.y() + << "\n"; + gcode_out += oss.str(); + } + } } return gcode_out; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 4b81b42aac..f2a67f600b 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -90,6 +90,7 @@ public: m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), + m_extruder_offsets(print_config.extruder_offset.values), m_priming(priming), m_tool_changes(tool_changes), m_final_purge(final_purge), @@ -107,14 +108,16 @@ private: WipeTowerIntegration& operator=(const WipeTowerIntegration&); std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const; - // Postprocesses gcode: rotates and moves all G1 extrusions and returns result - std::string rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const; + // Postprocesses gcode: rotates and moves G1 extrusions and returns result + std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; // Left / right edges of the wipe tower, for the planning of wipe moves. const float m_left; const float m_right; const Vec2f m_wipe_tower_pos; const float m_wipe_tower_rotation; + const std::vector m_extruder_offsets; + // Reference to cached values at the Printer class. const std::vector &m_priming; const std::vector> &m_tool_changes; diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 37e4040d19..354ec6d9e1 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -553,7 +553,7 @@ std::vector WipeTower::prime( result.elapsed_time = writer.elapsed_time(); result.extrusions = writer.extrusions(); result.start_pos = writer.start_pos_rotated(); - result.end_pos = writer.pos_rotated(); + result.end_pos = writer.pos(); results.push_back(std::move(result)); @@ -643,7 +643,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_ m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature); toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials. toolchange_Load(writer, cleaning_box); - writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road + writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area. ++ m_num_tool_changes; } else diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index c18a502b13..badb3e8b24 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -139,13 +139,15 @@ public: m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter - std::stringstream stream{m_semm ? ramming_parameters : std::string()}; - float speed = 0.f; - stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator; - m_filpar[idx].ramming_line_width_multiplicator /= 100; - m_filpar[idx].ramming_step_multiplicator /= 100; - while (stream >> speed) - m_filpar[idx].ramming_speed.push_back(speed); + if (m_semm) { + std::stringstream stream{ramming_parameters}; + float speed = 0.f; + stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator; + m_filpar[idx].ramming_line_width_multiplicator /= 100; + m_filpar[idx].ramming_step_multiplicator /= 100; + while (stream >> speed) + m_filpar[idx].ramming_speed.push_back(speed); + } m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later } @@ -241,8 +243,8 @@ public: int cooling_moves = 0; float cooling_initial_speed = 0.f; float cooling_final_speed = 0.f; - float ramming_line_width_multiplicator = 0.f; - float ramming_step_multiplicator = 0.f; + float ramming_line_width_multiplicator = 1.f; + float ramming_step_multiplicator = 1.f; float max_e_speed = std::numeric_limits::max(); std::vector ramming_speed; float nozzle_diameter; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 66532e9ead..21e1557936 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1971,4 +1971,4 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model) -#endif \ No newline at end of file +#endif diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2b4a6d978a..f03d531536 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -22,6 +22,8 @@ namespace cereal { class BinaryOutputArchive; template void load_optional(BinaryInputArchive &ar, std::shared_ptr &ptr); template void save_optional(BinaryOutputArchive &ar, const std::shared_ptr &ptr); + template void load_by_value(BinaryInputArchive &ar, T &obj); + template void save_by_value(BinaryOutputArchive &ar, const T &obj); } namespace Slic3r { @@ -31,6 +33,7 @@ class ModelInstance; class ModelMaterial; class ModelObject; class ModelVolume; +class ModelWipeTower; class Print; class SLAPrint; @@ -66,6 +69,21 @@ private: } }; +namespace Internal { + template + class StaticSerializationWrapper + { + public: + StaticSerializationWrapper(T &wrap) : wrapped(wrap) {} + private: + friend class cereal::access; + friend class UndoRedo::StackImpl; + template void load(Archive &ar) { cereal::load_by_value(ar, wrapped); } + template void save(Archive &ar) const { cereal::save_by_value(ar, wrapped); } + T& wrapped; + }; +} + typedef std::string t_model_material_id; typedef std::string t_model_material_attribute; typedef std::map t_model_material_attributes; @@ -140,7 +158,8 @@ private: ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); } template void serialize(Archive &ar) { assert(this->id().invalid()); assert(this->config.id().invalid()); - ar(attributes, config); + Internal::StaticSerializationWrapper config_wrapper(config); + ar(attributes, config_wrapper); // assert(this->id().valid()); assert(this->config.id().valid()); } @@ -349,7 +368,8 @@ private: } template void serialize(Archive &ar) { ar(cereal::base_class(this)); - ar(name, input_file, instances, volumes, config, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, + Internal::StaticSerializationWrapper config_wrapper(config); + ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); } }; @@ -535,7 +555,8 @@ private: } template void load(Archive &ar) { bool has_convex_hull; - ar(name, config, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::load_by_value(ar, config); assert(m_mesh); if (has_convex_hull) { cereal::load_optional(ar, m_convex_hull); @@ -547,7 +568,8 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, config, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + cereal::save_by_value(ar, config); if (has_convex_hull) cereal::save_optional(ar, m_convex_hull); } @@ -650,6 +672,35 @@ private: } }; +class ModelWipeTower final : public ObjectBase +{ +public: + Vec2d position; + double rotation; + +private: + friend class cereal::access; + friend class UndoRedo::StackImpl; + friend class Model; + + // Constructors to be only called by derived classes. + // Default constructor to assign a unique ID. + explicit ModelWipeTower() {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + explicit ModelWipeTower(int) : ObjectBase(-1) {} + // Copy constructor copies the ID. + explicit ModelWipeTower(const ModelWipeTower &cfg) = default; + + // Disabled methods. + ModelWipeTower(ModelWipeTower &&rhs) = delete; + ModelWipeTower& operator=(const ModelWipeTower &rhs) = delete; + ModelWipeTower& operator=(ModelWipeTower &&rhs) = delete; + + // For serialization / deserialization of ModelWipeTower composed into another class into the Undo / Redo stack as a separate object. + template void serialize(Archive &ar) { ar(position, rotation); } +}; + // The print bed content. // Description of a triangular model with multiple materials, multiple instances with various affine transformations // and with multiple modifier meshes. @@ -665,6 +716,8 @@ public: ModelMaterialMap materials; // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). ModelObjectPtrs objects; + // Wipe tower object. + ModelWipeTower wipe_tower; // Default constructor assigns a new ID to the model. Model() { assert(this->id().valid()); } @@ -742,7 +795,8 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; template void serialize(Archive &ar) { - ar(materials, objects); + Internal::StaticSerializationWrapper wipe_tower_wrapper(wipe_tower); + ar(materials, objects, wipe_tower_wrapper); } }; @@ -778,6 +832,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2); namespace cereal { template struct specialize {}; + template struct specialize {}; } #endif /* slic3r_Model_hpp_ */ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b4a79c6f04..c61fdca631 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1121,7 +1121,8 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_bed(bed) , m_camera(camera) , m_view_toolbar(view_toolbar) - , m_toolbar(GLToolbar::Normal, "Top") + , m_main_toolbar(GLToolbar::Normal, "Top") + , m_undoredo_toolbar(GLToolbar::Normal, "Top") , m_gizmos(*this) , m_use_clipping_planes(false) , m_sidebar_field("") @@ -1228,7 +1229,7 @@ bool GLCanvas3D::init() return false; } - if (m_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) + if (m_main_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) { std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl; return false; @@ -1242,7 +1243,7 @@ bool GLCanvas3D::init() if (m_gizmos.is_enabled() && !m_gizmos.init()) std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; - if (!_init_toolbar()) + if (!_init_toolbars()) return false; if (m_selection.is_enabled() && !m_selection.init()) @@ -1433,9 +1434,14 @@ void GLCanvas3D::enable_selection(bool enable) m_selection.set_enabled(enable); } -void GLCanvas3D::enable_toolbar(bool enable) +void GLCanvas3D::enable_main_toolbar(bool enable) { - m_toolbar.set_enabled(enable); + m_main_toolbar.set_enabled(enable); +} + +void GLCanvas3D::enable_undoredo_toolbar(bool enable) +{ + m_undoredo_toolbar.set_enabled(enable); } void GLCanvas3D::enable_dynamic_background(bool enable) @@ -1771,6 +1777,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re PrinterTechnology printer_technology = m_process->current_printer_technology(); int volume_idx_wipe_tower_old = -1; + if (printer_technology == ptSLA) + // Always do the full refresh in SLA mode to show / hide SLA support structures when an object is moved outside / inside the build volume. + m_regenerate_volumes = true; + if (m_regenerate_volumes) { // Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed). @@ -1803,7 +1813,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re state.step[istep].state = PrintStateBase::INVALID; else for (const ModelInstance *model_instance : print_object->model_object()->instances) - aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); + // Only the instances, which are currently printable, will have the SLA support structures kept. + // The instances outside the print bed will have the GLVolumes of their support structures released. + if (model_instance->is_printable()) + aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); } } sla_support_state.emplace_back(state); @@ -1955,7 +1968,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed for (GLVolume* volume : m_volumes.volumes) - volume->set_sla_shift_z(shift_zs[volume->object_idx()]); + if (volume->object_idx() < m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) + volume->set_sla_shift_z(shift_zs[volume->object_idx()]); } if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) @@ -2111,7 +2125,8 @@ void GLCanvas3D::load_sla_preview() if ((m_canvas != nullptr) && (print != nullptr)) { _set_current(); - _load_sla_shells(); + // Reload the SLA support structures into GLVolumes. + this->reload_scene(true, true); _update_sla_shells_outside_state(); _show_warning_texture_if_needed(WarningTexture::SlaSupportsOutside); } @@ -2214,7 +2229,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (!m_initialized) return; - m_dirty |= m_toolbar.update_items_state(); + m_dirty |= m_main_toolbar.update_items_state(); + m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); if (!m_dirty) @@ -2593,7 +2609,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ } - if (m_toolbar.on_mouse(evt, *this)) + if (m_main_toolbar.on_mouse(evt, *this)) + { + if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) + mouse_up_cleanup(); + m_mouse.set_start_position_3D_as_invalid(); + return; + } + + if (m_undoredo_toolbar.on_mouse(evt, *this)) { if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); @@ -2928,13 +2952,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Moving()) { m_mouse.position = pos.cast(); - std::string tooltip = ""; + std::string tooltip = L(""); if (tooltip.empty()) tooltip = m_gizmos.get_tooltip(); if (tooltip.empty()) - tooltip = m_toolbar.get_tooltip(); + tooltip = m_main_toolbar.get_tooltip(); + + if (tooltip.empty()) + tooltip = m_undoredo_toolbar.get_tooltip(); if (tooltip.empty()) tooltip = m_view_toolbar.get_tooltip(); @@ -3399,16 +3426,19 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) ImGuiWrapper* imgui = wxGetApp().imgui(); const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); - imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); + imgui->set_next_window_pos(x, m_undoredo_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); imgui->set_next_window_bg_alpha(0.5f); imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); int hovered = m_imgui_undo_redo_hovered_pos; int selected = -1; - const float em = static_cast(wxGetApp().em_unit()); + float em = static_cast(wxGetApp().em_unit()); +#if ENABLE_RETINA_GL + em *= m_retina_helper->get_scale_factor(); +#endif - if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) + if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected)) m_imgui_undo_redo_hovered_pos = hovered; else m_imgui_undo_redo_hovered_pos = -1; @@ -3421,9 +3451,20 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) imgui->end(); } -bool GLCanvas3D::_init_toolbar() +bool GLCanvas3D::_init_toolbars() { - if (!m_toolbar.is_enabled()) + if (!_init_main_toolbar()) + return false; + + if (!_init_undoredo_toolbar()) + return false; + + return true; +} + +bool GLCanvas3D::_init_main_toolbar() +{ + if (!m_main_toolbar.is_enabled()) return true; BackgroundTexture::Metadata background_data; @@ -3433,19 +3474,20 @@ bool GLCanvas3D::_init_toolbar() background_data.right = 16; background_data.bottom = 16; - if (!m_toolbar.init(background_data)) + if (!m_main_toolbar.init(background_data)) { // unable to init the toolbar texture, disable it - m_toolbar.set_enabled(false); + m_main_toolbar.set_enabled(false); return true; } -// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical); - m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); - m_toolbar.set_layout_orientation(GLToolbar::Layout::Top); - m_toolbar.set_border(5.0f); - m_toolbar.set_separator_size(5); - m_toolbar.set_gap_size(2); +// m_main_toolbar.set_layout_type(GLToolbar::Layout::Vertical); + m_main_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); + m_main_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right); + m_main_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); + m_main_toolbar.set_border(5.0f); + m_main_toolbar.set_separator_size(5); + m_main_toolbar.set_gap_size(2); GLToolbarItem::Data item; @@ -3454,7 +3496,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "delete"; @@ -3463,7 +3505,7 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 1; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "deleteall"; @@ -3472,7 +3514,7 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 2; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "arrange"; @@ -3481,10 +3523,10 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 3; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_arrange(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "copy"; @@ -3493,7 +3535,7 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 4; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "paste"; @@ -3502,10 +3544,10 @@ bool GLCanvas3D::_init_toolbar() item.sprite_id = 5; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "more"; @@ -3516,7 +3558,7 @@ bool GLCanvas3D::_init_toolbar() item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "fewer"; @@ -3526,10 +3568,10 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "splitobjects"; @@ -3539,7 +3581,7 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; item.name = "splitvolumes"; @@ -3549,10 +3591,10 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) + if (!m_main_toolbar.add_separator()) return false; item.name = "layersediting"; @@ -3563,35 +3605,98 @@ bool GLCanvas3D::_init_toolbar() item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; - if (!m_toolbar.add_item(item)) + if (!m_main_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) - return false; + return true; +} + +bool GLCanvas3D::_init_undoredo_toolbar() +{ + if (!m_undoredo_toolbar.is_enabled()) + return true; + + BackgroundTexture::Metadata background_data; + background_data.filename = "toolbar_background.png"; + background_data.left = 16; + background_data.top = 16; + background_data.right = 16; + background_data.bottom = 16; + + if (!m_undoredo_toolbar.init(background_data)) + { + // unable to init the toolbar texture, disable it + m_undoredo_toolbar.set_enabled(false); + return true; + } + +// m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Vertical); + m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); + m_undoredo_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left); + m_undoredo_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); + m_undoredo_toolbar.set_border(5.0f); + m_undoredo_toolbar.set_separator_size(5); + m_undoredo_toolbar.set_gap_size(2); + + GLToolbarItem::Data item; item.name = "undo"; item.icon_filename = "undo_toolbar.svg"; item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; - item.sprite_id = 11; - item.left.toggable = false; + item.sprite_id = 0; item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); }; item.right.toggable = true; item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; - item.visibility_callback = []()->bool { return true; }; - item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); }; - if (!m_toolbar.add_item(item)) + item.enabling_callback = [this]()->bool { + bool can_undo = wxGetApp().plater()->can_undo(); + unsigned int id = m_undoredo_toolbar.get_item_id("undo"); + + std::string curr_additional_tooltip; + m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip); + + std::string new_additional_tooltip = L(""); + if (can_undo) + wxGetApp().plater()->undo_redo_topmost_string_getter(true, new_additional_tooltip); + + if (new_additional_tooltip != curr_additional_tooltip) + { + m_undoredo_toolbar.set_additional_tooltip(id, new_additional_tooltip); + set_tooltip(L("")); + } + return can_undo; + }; + + if (!m_undoredo_toolbar.add_item(item)) return false; item.name = "redo"; item.icon_filename = "redo_toolbar.svg"; item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; - item.sprite_id = 12; + item.sprite_id = 1; item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); }; item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; - item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; - if (!m_toolbar.add_item(item)) + item.enabling_callback = [this]()->bool { + bool can_redo = wxGetApp().plater()->can_redo(); + unsigned int id = m_undoredo_toolbar.get_item_id("redo"); + + std::string curr_additional_tooltip; + m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip); + + std::string new_additional_tooltip = L(""); + if (can_redo) + wxGetApp().plater()->undo_redo_topmost_string_getter(false, new_additional_tooltip); + + if (new_additional_tooltip != curr_additional_tooltip) + { + m_undoredo_toolbar.set_additional_tooltip(id, new_additional_tooltip); + set_tooltip(L("")); + } + return can_redo; + }; + + if (!m_undoredo_toolbar.add_item(item)) return false; return true; @@ -3918,7 +4023,8 @@ void GLCanvas3D::_render_overlays() const _render_gizmos_overlay(); _render_warning_texture(); _render_legend_texture(); - _render_toolbar(); + _render_main_toolbar(); + _render_undoredo_toolbar(); _render_view_toolbar(); if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f)) @@ -4008,64 +4114,57 @@ void GLCanvas3D::_render_gizmos_overlay() const m_gizmos.render_overlay(); } -void GLCanvas3D::_render_toolbar() const +void GLCanvas3D::_render_main_toolbar() const { + if (!m_main_toolbar.is_enabled()) + return; + #if ENABLE_RETINA_GL -// m_toolbar.set_scale(m_retina_helper->get_scale_factor()); +// m_main_toolbar.set_scale(m_retina_helper->get_scale_factor()); const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true); - m_toolbar.set_scale(scale); //! #ys_FIXME_experiment + m_main_toolbar.set_scale(scale); //! #ys_FIXME_experiment #else -// m_toolbar.set_scale(m_canvas->GetContentScaleFactor()); -// m_toolbar.set_scale(wxGetApp().em_unit()*0.1f); +// m_main_toolbar.set_scale(m_canvas->GetContentScaleFactor()); +// m_main_toolbar.set_scale(wxGetApp().em_unit()*0.1f); const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true)); - m_toolbar.set_icons_size(size); //! #ys_FIXME_experiment + m_main_toolbar.set_icons_size(size); //! #ys_FIXME_experiment #endif // ENABLE_RETINA_GL Size cnv_size = get_canvas_size(); float zoom = (float)m_camera.get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); + float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; + float left = -0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width()) * inv_zoom; - float top = 0.0f; - float left = 0.0f; - switch (m_toolbar.get_layout_type()) - { - default: - case GLToolbar::Layout::Horizontal: - { - // centers the toolbar on the top edge of the 3d scene - if (orientation == GLToolbar::Layout::Top) - { - top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - left = -0.5f * m_toolbar.get_width() * inv_zoom; - } - else - { - top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom; - left = -0.5f * m_toolbar.get_width() * inv_zoom; - } - break; - } - case GLToolbar::Layout::Vertical: - { - // centers the toolbar on the right edge of the 3d scene - if (orientation == GLToolbar::Layout::Left) - { - top = 0.5f * m_toolbar.get_height() * inv_zoom; - left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; - } - else - { - top = 0.5f * m_toolbar.get_height() * inv_zoom; - left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; - } - break; - } - } - m_toolbar.set_position(top, left); + m_main_toolbar.set_position(top, left); + m_main_toolbar.render(*this); +} - m_toolbar.render(*this); +void GLCanvas3D::_render_undoredo_toolbar() const +{ + if (!m_undoredo_toolbar.is_enabled()) + return; + +#if ENABLE_RETINA_GL +// m_undoredo_toolbar.set_scale(m_retina_helper->get_scale_factor()); + const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true); + m_undoredo_toolbar.set_scale(scale); //! #ys_FIXME_experiment +#else +// m_undoredo_toolbar.set_scale(m_canvas->GetContentScaleFactor()); +// m_undoredo_toolbar.set_scale(wxGetApp().em_unit()*0.1f); + const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true)); + m_undoredo_toolbar.set_icons_size(size); //! #ys_FIXME_experiment +#endif // ENABLE_RETINA_GL + + Size cnv_size = get_canvas_size(); + float zoom = (float)m_camera.get_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; + float left = (m_main_toolbar.get_width() - 0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width())) * inv_zoom; + m_undoredo_toolbar.set_position(top, left); + m_undoredo_toolbar.render(*this); } void GLCanvas3D::_render_view_toolbar() const @@ -5322,56 +5421,6 @@ void GLCanvas3D::_load_fff_shells() } } -void GLCanvas3D::_load_sla_shells() -{ - //FIXME use reload_scene -#if 1 - const SLAPrint* print = this->sla_print(); - if (print->objects().empty()) - // nothing to render, return - return; - - auto add_volume = [this](const SLAPrintObject &object, int volume_id, const SLAPrintObject::Instance& instance, - const TriangleMesh &mesh, const float color[4], bool outside_printer_detection_enabled) { - m_volumes.volumes.emplace_back(new GLVolume(color)); - GLVolume& v = *m_volumes.volumes.back(); - v.indexed_vertex_array.load_mesh(mesh); - v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; - v.composite_id.volume_id = volume_id; - v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0)); - v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation)); - v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.); - v.set_convex_hull(mesh.convex_hull_3d()); - }; - - // adds objects' volumes - for (const SLAPrintObject* obj : print->objects()) - if (obj->is_step_done(slaposSliceSupports)) { - unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); - for (const SLAPrintObject::Instance& instance : obj->instances()) { - add_volume(*obj, 0, instance, obj->transformed_mesh(), GLVolume::MODEL_COLOR[0], true); - // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when - // through the update_volumes_colors_by_extruder() call. - m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); - if (obj->is_step_done(slaposSupportTree) && obj->has_mesh(slaposSupportTree)) - add_volume(*obj, -int(slaposSupportTree), instance, obj->support_mesh(), GLVolume::SLA_SUPPORT_COLOR, true); - if (obj->is_step_done(slaposBasePool) && obj->has_mesh(slaposBasePool)) - add_volume(*obj, -int(slaposBasePool), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, false); - } - double shift_z = obj->get_current_elevation(); - for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { - GLVolume& v = *m_volumes.volumes[i]; - // apply shift z - v.set_sla_shift_z(shift_z); - } - } - - update_volumes_colors_by_extruder(); -#else - this->reload_scene(true, true); -#endif -} - void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data) { unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); @@ -5587,14 +5636,14 @@ void GLCanvas3D::_update_selection_from_hover() bool GLCanvas3D::_deactivate_undo_redo_toolbar_items() { - if (m_toolbar.is_item_pressed("undo")) + if (m_undoredo_toolbar.is_item_pressed("undo")) { - m_toolbar.force_right_action(m_toolbar.get_item_id("undo"), *this); + m_undoredo_toolbar.force_right_action(m_undoredo_toolbar.get_item_id("undo"), *this); return true; } - else if (m_toolbar.is_item_pressed("redo")) + else if (m_undoredo_toolbar.is_item_pressed("redo")) { - m_toolbar.force_right_action(m_toolbar.get_item_id("redo"), *this); + m_undoredo_toolbar.force_right_action(m_undoredo_toolbar.get_item_id("redo"), *this); return true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index cdf45c4f7c..b6f0843a22 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -412,7 +412,8 @@ private: Shader m_shader; Mouse m_mouse; mutable GLGizmosManager m_gizmos; - mutable GLToolbar m_toolbar; + mutable GLToolbar m_main_toolbar; + mutable GLToolbar m_undoredo_toolbar; ClippingPlane m_clipping_planes[2]; mutable ClippingPlane m_camera_clipping_plane; bool m_use_clipping_planes; @@ -525,7 +526,8 @@ public: void enable_moving(bool enable); void enable_gizmos(bool enable); void enable_selection(bool enable); - void enable_toolbar(bool enable); + void enable_main_toolbar(bool enable); + void enable_undoredo_toolbar(bool enable); void enable_dynamic_background(bool enable); void allow_multisample(bool allow); @@ -618,10 +620,18 @@ public: void start_keeping_dirty() { m_keep_dirty = true; } void stop_keeping_dirty() { m_keep_dirty = false; } + unsigned int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } + void force_main_toolbar_left_action(unsigned int item_id) { m_main_toolbar.force_left_action(item_id, *this); } + void force_main_toolbar_right_action(unsigned int item_id) { m_main_toolbar.force_right_action(item_id, *this); } + void get_undoredo_toolbar_additional_tooltip(unsigned int item_id, std::string& text) { return m_undoredo_toolbar.get_additional_tooltip(item_id, text); } + void set_undoredo_toolbar_additional_tooltip(unsigned int item_id, const std::string& text) { m_undoredo_toolbar.set_additional_tooltip(item_id, text); } + private: bool _is_shown_on_screen() const; - bool _init_toolbar(); + bool _init_toolbars(); + bool _init_main_toolbar(); + bool _init_undoredo_toolbar(); bool _set_current(); void _resize(unsigned int w, unsigned int h); @@ -648,7 +658,8 @@ private: void _render_volumes_for_picking() const; void _render_current_gizmo() const; void _render_gizmos_overlay() const; - void _render_toolbar() const; + void _render_main_toolbar() const; + void _render_undoredo_toolbar() const; void _render_view_toolbar() const; #if ENABLE_SHOW_CAMERA_TARGET void _render_camera_target() const; @@ -695,8 +706,6 @@ private: void _load_gcode_unretractions(const GCodePreviewData& preview_data); // generates objects and wipe tower geometry void _load_fff_shells(); - // generates objects geometry for sla - void _load_sla_shells(); // sets gcode geometry visibility according to user selection void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); void _update_toolpath_volumes_outside_state(); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index f4748f680e..63387bf2db 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -48,6 +48,7 @@ GLToolbarItem::Data::Data() : name("") , icon_filename("") , tooltip("") + , additional_tooltip("") , sprite_id(-1) , visible(true) , visibility_callback(Default_Visibility_Callback) @@ -131,7 +132,8 @@ const float GLToolbar::Default_Icons_Size = 40.0f; GLToolbar::Layout::Layout() : type(Horizontal) - , orientation(Center) + , horizontal_orientation(HO_Center) + , vertical_orientation(VO_Center) , top(0.0f) , left(0.0f) , border(0.0f) @@ -191,16 +193,6 @@ void GLToolbar::set_layout_type(GLToolbar::Layout::EType type) m_layout.dirty = true; } -GLToolbar::Layout::EOrientation GLToolbar::get_layout_orientation() const -{ - return m_layout.orientation; -} - -void GLToolbar::set_layout_orientation(GLToolbar::Layout::EOrientation orientation) -{ - m_layout.orientation = orientation; -} - void GLToolbar::set_position(float top, float left) { m_layout.top = top; @@ -374,6 +366,31 @@ void GLToolbar::force_right_action(unsigned int item_id, GLCanvas3D& parent) do_action(GLToolbarItem::Right, item_id, parent, false); } +void GLToolbar::get_additional_tooltip(unsigned int item_id, std::string& text) +{ + if (item_id < (unsigned int)m_items.size()) + { + GLToolbarItem* item = m_items[item_id]; + if (item != nullptr) + { + text = item->get_additional_tooltip(); + return; + } + } + + text = L(""); +} + +void GLToolbar::set_additional_tooltip(unsigned int item_id, const std::string& text) +{ + if (item_id < (unsigned int)m_items.size()) + { + GLToolbarItem* item = m_items[item_id]; + if (item != nullptr) + item->set_additional_tooltip(text); + } +} + bool GLToolbar::update_items_state() { bool ret = false; @@ -403,6 +420,9 @@ void GLToolbar::render(const GLCanvas3D& parent) const bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) { + if (!m_enabled) + return false; + Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); bool processed = false; @@ -433,7 +453,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) if (item_id == -1) { // mouse is outside the toolbar - m_tooltip = ""; + m_tooltip = L(""); } else { @@ -600,7 +620,7 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) { if (!m_enabled) - return ""; + return L(""); switch (m_layout.type) { @@ -649,7 +669,15 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC GLToolbarItem::EState state = item->get_state(); bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); if (inside) + { tooltip = item->get_tooltip(); + if (!item->is_pressed()) + { + const std::string& additional_tooltip = item->get_additional_tooltip(); + if (!additional_tooltip.empty()) + tooltip += L("\n") + additional_tooltip; + } + } switch (state) { @@ -745,7 +773,15 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan GLToolbarItem::EState state = item->get_state(); bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); if (inside) + { tooltip = item->get_tooltip(); + if (!item->is_pressed()) + { + const std::string& additional_tooltip = item->get_additional_tooltip(); + if (!additional_tooltip.empty()) + tooltip += L("\n") + additional_tooltip; + } + } switch (state) { @@ -966,6 +1002,84 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& return -1; } +void GLToolbar::render_background(float left, float top, float right, float bottom, float border) const +{ + unsigned int tex_id = m_background_texture.texture.get_id(); + float tex_width = (float)m_background_texture.texture.get_width(); + float tex_height = (float)m_background_texture.texture.get_height(); + if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) + { + float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; + float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; + + float internal_left = left + border; + float internal_right = right - border; + float internal_top = top - border; + float internal_bottom = bottom + border; + + float left_uv = 0.0f; + float right_uv = 1.0f; + float top_uv = 1.0f; + float bottom_uv = 0.0f; + + float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; + float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; + float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; + float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; + + // top-left corner + if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Top)) + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { left_uv, internal_top_uv }, { internal_left_uv, internal_top_uv }, { internal_left_uv, top_uv }, { left_uv, top_uv } }); + + // top edge + if (m_layout.vertical_orientation == Layout::VO_Top) + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } }); + + // top-right corner + if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Top)) + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } }); + + // center-left edge + if (m_layout.horizontal_orientation == Layout::HO_Left) + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { left_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { left_uv, internal_top_uv } }); + + // center + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + + // center-right edge + if (m_layout.horizontal_orientation == Layout::HO_Right) + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } }); + + // bottom-left corner + if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Bottom)) + GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { left_uv, bottom_uv }, { internal_left_uv, bottom_uv }, { internal_left_uv, internal_bottom_uv }, { left_uv, internal_bottom_uv } }); + + // bottom edge + if (m_layout.vertical_orientation == Layout::VO_Bottom) + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); + + // bottom-right corner + if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Bottom)) + GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } }); + } +} + void GLToolbar::render_horizontal(const GLCanvas3D& parent) const { unsigned int tex_id = m_icons_texture.get_id(); @@ -991,88 +1105,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const float right = left + scaled_width; float bottom = top - scaled_height; - // renders background - unsigned int bg_tex_id = m_background_texture.texture.get_id(); - float bg_tex_width = (float)m_background_texture.texture.get_width(); - float bg_tex_height = (float)m_background_texture.texture.get_height(); - if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0)) - { - float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f; - float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f; - - float bg_uv_left = 0.0f; - float bg_uv_right = 1.0f; - float bg_uv_top = 1.0f; - float bg_uv_bottom = 0.0f; - - float bg_left = left; - float bg_right = right; - float bg_top = top; - float bg_bottom = bottom; - float bg_width = right - left; - float bg_height = top - bottom; - float bg_min_size = std::min(bg_width, bg_height); - - float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; - float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; - float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height; - float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height; - - float bg_i_left = bg_left + scaled_border; - float bg_i_right = bg_right - scaled_border; - float bg_i_top = bg_top - scaled_border; - float bg_i_bottom = bg_bottom + scaled_border; - - switch (m_layout.orientation) - { - case Layout::Top: - { - bg_uv_top = bg_uv_i_top; - bg_i_top = bg_top; - break; - } - case Layout::Bottom: - { - bg_uv_bottom = bg_uv_i_bottom; - bg_i_bottom = bg_bottom; - break; - } - case Layout::Center: - { - break; - } - }; - - if ((m_layout.border > 0) && (bg_uv_top != bg_uv_i_top)) - { - if (bg_uv_left != bg_uv_i_left) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } }); - - if (bg_uv_right != bg_uv_i_right) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); - } - - if ((m_layout.border > 0) && (bg_uv_left != bg_uv_i_left)) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); - - if ((m_layout.border > 0) && (bg_uv_right != bg_uv_i_right)) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); - - if ((m_layout.border > 0) && (bg_uv_bottom != bg_uv_i_bottom)) - { - if (bg_uv_left != bg_uv_i_left) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } }); - - if (bg_uv_right != bg_uv_i_right) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } }); - } - } + render_background(left, top, right, bottom, scaled_border); left += scaled_border; top -= scaled_border; @@ -1121,88 +1154,7 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const float right = left + scaled_width; float bottom = top - scaled_height; - // renders background - unsigned int bg_tex_id = m_background_texture.texture.get_id(); - float bg_tex_width = (float)m_background_texture.texture.get_width(); - float bg_tex_height = (float)m_background_texture.texture.get_height(); - if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0)) - { - float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f; - float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f; - - float bg_uv_left = 0.0f; - float bg_uv_right = 1.0f; - float bg_uv_top = 1.0f; - float bg_uv_bottom = 0.0f; - - float bg_left = left; - float bg_right = right; - float bg_top = top; - float bg_bottom = bottom; - float bg_width = right - left; - float bg_height = top - bottom; - float bg_min_size = std::min(bg_width, bg_height); - - float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; - float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; - float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height; - float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height; - - float bg_i_left = bg_left + scaled_border; - float bg_i_right = bg_right - scaled_border; - float bg_i_top = bg_top - scaled_border; - float bg_i_bottom = bg_bottom + scaled_border; - - switch (m_layout.orientation) - { - case Layout::Left: - { - bg_uv_left = bg_uv_i_left; - bg_i_left = bg_left; - break; - } - case Layout::Right: - { - bg_uv_right = bg_uv_i_right; - bg_i_right = bg_right; - break; - } - case Layout::Center: - { - break; - } - }; - - if ((m_layout.border > 0) && (bg_uv_top != bg_uv_i_top)) - { - if (bg_uv_left != bg_uv_i_left) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } }); - - if (bg_uv_right != bg_uv_i_right) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); - } - - if ((m_layout.border > 0) && (bg_uv_left != bg_uv_i_left)) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); - - if ((m_layout.border > 0) && (bg_uv_right != bg_uv_i_right)) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); - - if ((m_layout.border > 0) && (bg_uv_bottom != bg_uv_i_bottom)) - { - if (bg_uv_left != bg_uv_i_left) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } }); - - if (bg_uv_right != bg_uv_i_right) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } }); - } - } + render_background(left, top, right, bottom, scaled_border); left += scaled_border; top -= scaled_border; diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 875b2f9f69..527317e589 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -80,6 +80,7 @@ public: std::string name; std::string icon_filename; std::string tooltip; + std::string additional_tooltip; unsigned int sprite_id; // mouse left click Option left; @@ -112,6 +113,8 @@ public: const std::string& get_name() const { return m_data.name; } const std::string& get_icon_filename() const { return m_data.icon_filename; } const std::string& get_tooltip() const { return m_data.tooltip; } + const std::string& get_additional_tooltip() const { return m_data.additional_tooltip; } + void set_additional_tooltip(const std::string& text) { m_data.additional_tooltip = text; } void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); } void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); } @@ -189,18 +192,25 @@ public: Num_Types }; - enum EOrientation : unsigned int + enum EHorizontalOrientation : unsigned char { - Top, - Bottom, - Left, - Right, - Center, - Num_Locations + HO_Left, + HO_Center, + HO_Right, + Num_Horizontal_Orientations + }; + + enum EVerticalOrientation : unsigned char + { + VO_Top, + VO_Center, + VO_Bottom, + Num_Vertical_Orientations }; EType type; - EOrientation orientation; + EHorizontalOrientation horizontal_orientation; + EVerticalOrientation vertical_orientation; float top; float left; float border; @@ -254,8 +264,10 @@ public: Layout::EType get_layout_type() const; void set_layout_type(Layout::EType type); - Layout::EOrientation get_layout_orientation() const; - void set_layout_orientation(Layout::EOrientation orientation); + Layout::EHorizontalOrientation get_horizontal_orientation() const { return m_layout.horizontal_orientation; } + void set_horizontal_orientation(Layout::EHorizontalOrientation orientation) { m_layout.horizontal_orientation = orientation; } + Layout::EVerticalOrientation get_vertical_orientation() const { return m_layout.vertical_orientation; } + void set_vertical_orientation(Layout::EVerticalOrientation orientation) { m_layout.vertical_orientation = orientation; } void set_position(float top, float left); void set_border(float border); @@ -288,6 +300,9 @@ public: const std::string& get_tooltip() const { return m_tooltip; } + void get_additional_tooltip(unsigned int item_id, std::string& text); + void set_additional_tooltip(unsigned int item_id, const std::string& text); + // returns true if any item changed its state bool update_items_state(); @@ -311,6 +326,7 @@ private: int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; + void render_background(float left, float top, float right, float bottom, float border) const; void render_horizontal(const GLCanvas3D& parent) const; void render_vertical(const GLCanvas3D& parent) const; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 308b3f208d..0a2877d922 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -199,18 +199,17 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off.png"); m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); - for (const std::string axis : { "x", "y", "z" }) { - const std::string label = boost::algorithm::to_upper_copy(axis); - def.set_default_value(new ConfigOptionString{ " " + label }); - Option option = Option(def, axis + "_axis_legend"); - - unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2 + static const char axes[] = { 'X', 'Y', 'Z' }; + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) { + const char label = axes[axis_idx]; + def.set_default_value(new ConfigOptionString{ std::string(" ") + label }); + Option option(def, std::string() + label + "_axis_legend"); // We will add a button to toggle mirroring to each axis: - auto mirror_button = [this, mirror_btn_width, axis_idx, &label](wxWindow* parent) { + auto mirror_button = [this, mirror_btn_width, axis_idx, label](wxWindow* parent) { wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width); auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); - btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label)); + btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label)); m_mirror_buttons[axis_idx].first = btn; m_mirror_buttons[axis_idx].second = mbShown; @@ -245,7 +244,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : canvas->do_mirror(L("Set Mirror")); UpdateAndShow(true); }); - return sizer; + + return sizer; }; option.side_widget = mirror_button; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0ba447c1bc..36354ab240 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -63,7 +63,8 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_ m_canvas->set_config(config); m_canvas->enable_gizmos(true); m_canvas->enable_selection(true); - m_canvas->enable_toolbar(true); + m_canvas->enable_main_toolbar(true); + m_canvas->enable_undoredo_toolbar(true); wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1509c1328b..dc819961f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -879,6 +879,60 @@ void GLGizmosManager::reset() m_gizmos.clear(); } +void GLGizmosManager::render_background(float left, float top, float right, float bottom, float border) const +{ + unsigned int tex_id = m_background_texture.texture.get_id(); + float tex_width = (float)m_background_texture.texture.get_width(); + float tex_height = (float)m_background_texture.texture.get_height(); + if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) + { + float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; + float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; + + float internal_left = left + border; + float internal_right = right - border; + float internal_top = top - border; + float internal_bottom = bottom + border; + + float left_uv = 0.0f; + float right_uv = 1.0f; + float top_uv = 1.0f; + float bottom_uv = 0.0f; + + float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; + float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; + float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; + float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; + + // top-left corner + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + + // top edge + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } }); + + // top-right corner + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } }); + + // center-left edge + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + + // center + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + + // center-right edge + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } }); + + // bottom-left corner + GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + + // bottom edge + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); + + // bottom-right corner + GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } }); + } +} + void GLGizmosManager::do_render_overlay() const { if (m_gizmos.empty()) @@ -901,71 +955,7 @@ void GLGizmosManager::do_render_overlay() const float right = left + width * inv_zoom; float bottom = top - height * inv_zoom; - // renders background - unsigned int bg_tex_id = m_background_texture.texture.get_id(); - float bg_tex_width = (float)m_background_texture.texture.get_width(); - float bg_tex_height = (float)m_background_texture.texture.get_height(); - if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0)) - { - float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f; - float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f; - - float bg_uv_left = 0.0f; - float bg_uv_right = 1.0f; - float bg_uv_top = 1.0f; - float bg_uv_bottom = 0.0f; - - float bg_left = left; - float bg_right = right; - float bg_top = top; - float bg_bottom = bottom; - float bg_width = right - left; - float bg_height = top - bottom; - float bg_min_size = std::min(bg_width, bg_height); - - float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; - float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; - float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height; - float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height; - - float bg_i_left = bg_left + scaled_border; - float bg_i_right = bg_right - scaled_border; - float bg_i_top = bg_top - scaled_border; - float bg_i_bottom = bg_bottom + scaled_border; - - bg_uv_left = bg_uv_i_left; - bg_i_left = bg_left; - - if ((m_overlay_border > 0) && (bg_uv_top != bg_uv_i_top)) - { - if (bg_uv_left != bg_uv_i_left) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } }); - - if (bg_uv_right != bg_uv_i_right) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); - } - - if ((m_overlay_border > 0) && (bg_uv_left != bg_uv_i_left)) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); - - if ((m_overlay_border > 0) && (bg_uv_right != bg_uv_i_right)) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); - - if ((m_overlay_border > 0) && (bg_uv_bottom != bg_uv_i_bottom)) - { - if (bg_uv_left != bg_uv_i_left) - GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); - - GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } }); - - if (bg_uv_right != bg_uv_i_right) - GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } }); - } - } + render_background(left, top, right, bottom, scaled_border); top_x += scaled_border; top_y -= scaled_border; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 803613ec74..e1978e60dd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -187,6 +187,7 @@ public: private: void reset(); + void render_background(float left, float top, float right, float bottom, float border) const; void do_render_overlay() const; float get_total_overlay_height() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0c65270fff..ed6677732b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3430,7 +3430,8 @@ void Plater::priv::init_view_toolbar() if (!view_toolbar.init(background_data)) return; - view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom); + view_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left); + view_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Bottom); view_toolbar.set_border(5.0f); view_toolbar.set_gap_size(1.0f); @@ -3594,6 +3595,13 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) unsigned int flags = 0; if (this->view3D->is_layers_editing_enabled()) flags |= UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE; + //FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config. + // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. + if (this->printer_technology == ptFFF) { + const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); + model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); + } this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, flags); this->undo_redo_stack.release_least_recently_used(); // Save the last active preset name of a particular printer technology. @@ -3640,6 +3648,13 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator } // Save the last active preset name of a particular printer technology. ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); + //FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config. + // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. + if (this->printer_technology == ptFFF) { + const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y")); + model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle"); + } // Flags made of Snapshot::Flags enum values. unsigned int new_flags = it_snapshot->flags; unsigned int top_snapshot_flags = 0; @@ -3647,8 +3662,8 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator top_snapshot_flags |= UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE; bool new_variable_layer_editing_active = (new_flags & UndoRedo::Snapshot::VARIABLE_LAYER_EDITING_ACTIVE) != 0; // Disable layer editing before the Undo / Redo jump. - if (! new_variable_layer_editing_active && view3D->is_layers_editing_enabled()) - view3D->enable_layers_editing(false); + if (!new_variable_layer_editing_active && view3D->is_layers_editing_enabled()) + view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); // Do the jump in time. if (it_snapshot->timestamp < this->undo_redo_stack.active_snapshot_time() ? this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->printer_technology, top_snapshot_flags, it_snapshot->timestamp) : @@ -3662,11 +3677,27 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator // This also switches the printer technology based on the printer technology of the active printer profile. wxGetApp().load_current_presets(); } - this->update_after_undo_redo(temp_snapshot_was_taken); + //FIXME updating the Print config from the Wipe tower config values at the ModelWipeTower. + // This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config. + if (this->printer_technology == ptFFF) { + const DynamicPrintConfig ¤t_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + Vec2d current_position(current_config.opt_float("wipe_tower_x"), current_config.opt_float("wipe_tower_y")); + double current_rotation = current_config.opt_float("wipe_tower_rotation_angle"); + if (current_position != model.wipe_tower.position || current_rotation != model.wipe_tower.rotation) { + DynamicPrintConfig new_config; + new_config.set_key_value("wipe_tower_x", new ConfigOptionFloat(model.wipe_tower.position.x())); + new_config.set_key_value("wipe_tower_y", new ConfigOptionFloat(model.wipe_tower.position.y())); + new_config.set_key_value("wipe_tower_rotation_angle", new ConfigOptionFloat(model.wipe_tower.rotation)); + Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); + tab_print->load_config(new_config); + tab_print->update_dirty(); + } + } + this->update_after_undo_redo(temp_snapshot_was_taken); // Enable layer editing after the Undo / Redo jump. if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active) - view3D->enable_layers_editing(true); - } + view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); + } } void Plater::priv::update_after_undo_redo(bool /* temp_snapshot_was_taken */) @@ -4278,6 +4309,19 @@ bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** o return false; } +void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& out_text) +{ + const std::vector& ss_stack = p->undo_redo_stack.snapshots(); + const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -1 : 0); + + if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) { + out_text = ss_stack[idx_in_ss_stack].name; + return; + } + + out_text = L(""); +} + void Plater::on_extruders_change(int num_extruders) { auto& choices = sidebar().combos_filament(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index d7e91f516c..e0737c6b9f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -196,6 +196,7 @@ public: void undo_to(int selection); void redo_to(int selection); bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text); + void undo_redo_topmost_string_getter(const bool is_undo, std::string& out_text); const Slic3r::UndoRedo::Stack& undo_redo_stack() const; void on_extruders_change(int extruders_count); diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 2605bd2a74..a8f9cc134a 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -514,11 +514,11 @@ public: const Selection& selection_deserialized() const { return m_selection; } //protected: - template ObjectID save_mutable_object(const T &object); + template ObjectID save_mutable_object(const T &object); template ObjectID save_immutable_object(std::shared_ptr &object, bool optional); template T* load_mutable_object(const Slic3r::ObjectID id); template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id, bool optional); - template void load_mutable_object(const Slic3r::ObjectID id, T &target); + template void load_mutable_object(const Slic3r::ObjectID id, T &target); #ifdef SLIC3R_UNDOREDO_DEBUG std::string format() const { @@ -601,7 +601,6 @@ class ModelObject; class ModelVolume; class ModelInstance; class ModelMaterial; -class ModelConfig; class DynamicPrintConfig; class TriangleMesh; @@ -616,14 +615,13 @@ namespace cereal template struct specialize {}; template struct specialize {}; template struct specialize {}; - template struct specialize {}; template struct specialize, cereal::specialization::non_member_load_save> {}; // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. template void save(BinaryOutputArchive& ar, T* const& ptr) { - ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); + ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); } // Load ObjectBase derived class from the Undo / Redo stack as a separate object @@ -655,19 +653,18 @@ namespace cereal // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. - void save(BinaryOutputArchive& ar, const Slic3r::ModelConfig &cfg) + template void save_by_value(BinaryOutputArchive& ar, const T &cfg) { - ar(cereal::get_user_data(ar).save_mutable_object(cfg)); + ar(cereal::get_user_data(ar).save_mutable_object(cfg)); } - // Load ObjectBase derived class from the Undo / Redo stack as a separate object // based on the ObjectID loaded from this stream. - void load(BinaryInputArchive& ar, Slic3r::ModelConfig &cfg) + template void load_by_value(BinaryInputArchive& ar, T &cfg) { Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); size_t id; ar(id); - stack.load_mutable_object(Slic3r::ObjectID(id), cfg); + stack.load_mutable_object(Slic3r::ObjectID(id), cfg); } // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, @@ -723,7 +720,7 @@ template std::shared_ptr& ImmutableObjectHistory::share return m_shared_object; } -template ObjectID StackImpl::save_mutable_object(const T &object) +template ObjectID StackImpl::save_mutable_object(const T &object) { // First find or allocate a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(object.id()); @@ -734,7 +731,7 @@ template ObjectID StackImpl::save_mutable_object(cons std::ostringstream oss; { Slic3r::UndoRedo::OutputArchive archive(*this, oss); - archive(static_cast(object)); + archive(object); } object_history->save(m_active_snapshot_time, m_current_time, oss.str()); return object.id(); @@ -758,7 +755,7 @@ template ObjectID StackImpl::save_immutable_object(std::shared_ptr T* StackImpl::load_mutable_object(const Slic3r::ObjectID id) { T *target = new T(); - this->load_mutable_object(id, *target); + this->load_mutable_object(id, *target); return target; } @@ -775,7 +772,7 @@ template std::shared_ptr StackImpl::load_immutable_object(c return object_history->shared_ptr(*this); } -template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) +template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) { // First find a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(id); @@ -785,7 +782,7 @@ template void StackImpl::load_mutable_object(const Sl std::istringstream iss(object_history->load(m_active_snapshot_time)); Slic3r::UndoRedo::InputArchive archive(*this, iss); target.m_id = id; - archive(static_cast(target)); + archive(target); } // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. @@ -800,14 +797,14 @@ void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Mo m_snapshots.erase(it, m_snapshots.end()); } // Take new snapshots. - this->save_mutable_object(model); + this->save_mutable_object(model); m_selection.volumes_and_instances.clear(); m_selection.volumes_and_instances.reserve(selection.get_volume_idxs().size()); m_selection.mode = selection.get_mode(); for (unsigned int volume_idx : selection.get_volume_idxs()) m_selection.volumes_and_instances.emplace_back(selection.get_volume(volume_idx)->geometry_id); - this->save_mutable_object(m_selection); - this->save_mutable_object(gizmos); + this->save_mutable_object(m_selection); + this->save_mutable_object(gizmos); // Save the snapshot info. m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id, printer_technology, flags); m_active_snapshot_time = m_current_time; @@ -833,12 +830,12 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GU m_active_snapshot_time = timestamp; model.clear_objects(); model.clear_materials(); - this->load_mutable_object(ObjectID(it_snapshot->model_id), model); + this->load_mutable_object(ObjectID(it_snapshot->model_id), model); model.update_links_bottom_up_recursive(); m_selection.volumes_and_instances.clear(); - this->load_mutable_object(m_selection.id(), m_selection); + this->load_mutable_object(m_selection.id(), m_selection); gizmos.reset_all_states(); - this->load_mutable_object(gizmos.id(), gizmos); + this->load_mutable_object(gizmos.id(), gizmos); // Sort the volumes so that we may use binary search. std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end()); this->m_active_snapshot_time = timestamp;