mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge remote-tracking branch 'remotes/origin/lh_multi_material_segmentation' into vb_print_regions
This commit is contained in:
		
						commit
						980ca195f5
					
				
					 39 changed files with 2691 additions and 146 deletions
				
			
		|  | @ -57,6 +57,8 @@ set(SLIC3R_GUI_SOURCES | |||
|     GUI/Gizmos/GLGizmoPainterBase.hpp | ||||
|     GUI/Gizmos/GLGizmoSeam.cpp | ||||
|     GUI/Gizmos/GLGizmoSeam.hpp | ||||
|     GUI/Gizmos/GLGizmoMmuSegmentation.cpp | ||||
|     GUI/Gizmos/GLGizmoMmuSegmentation.hpp | ||||
|     GUI/GLSelectionRectangle.cpp | ||||
|     GUI/GLSelectionRectangle.hpp | ||||
|     GUI/GLModel.hpp | ||||
|  |  | |||
|  | @ -259,7 +259,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const | |||
|     ImGui::SameLine(); | ||||
|     float widget_align = ImGui::GetCursorPosX(); | ||||
|     ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); | ||||
|     m_adaptive_quality = clamp(0.0f, 1.f, m_adaptive_quality); | ||||
|     m_adaptive_quality = std::clamp(m_adaptive_quality, 0.0f, 1.f); | ||||
|     ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f"); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|  | @ -1062,7 +1062,8 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject | |||
|                     const GLGizmosManager& gm = get_gizmos_manager(); | ||||
|                     auto gizmo_type = gm.get_current_type(); | ||||
|                     if (    (gizmo_type == GLGizmosManager::FdmSupports | ||||
|                           || gizmo_type == GLGizmosManager::Seam) | ||||
|                           || gizmo_type == GLGizmosManager::Seam | ||||
|                           || gizmo_type == GLGizmosManager::MmuSegmentation) | ||||
|                         && ! vol->is_modifier) | ||||
|                         vol->force_neutral_color = true; | ||||
|                     else | ||||
|  | @ -2926,7 +2927,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
|         else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { | ||||
|             if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports | ||||
|              && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports | ||||
|              && m_gizmos.get_current_type() != GLGizmosManager::Seam) { | ||||
|              && m_gizmos.get_current_type() != GLGizmosManager::Seam | ||||
|              && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { | ||||
|                 m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); | ||||
|                 m_dirty = true; | ||||
|             } | ||||
|  | @ -4808,7 +4810,8 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const | |||
|             (m_gizmos.get_current_type() != GLGizmosManager::FdmSupports | ||||
|           && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports | ||||
|           && m_gizmos.get_current_type() != GLGizmosManager::Hollow | ||||
|           && m_gizmos.get_current_type() != GLGizmosManager::Seam); | ||||
|           && m_gizmos.get_current_type() != GLGizmosManager::Seam | ||||
|           && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation); | ||||
| 
 | ||||
|     wxGetApp().plater()->get_bed().render(const_cast<GLCanvas3D&>(*this), bottom, scale_factor, show_axes, show_texture); | ||||
| } | ||||
|  | @ -4868,7 +4871,8 @@ void GLCanvas3D::_render_objects() const | |||
|             const GLGizmosManager& gm = get_gizmos_manager(); | ||||
|             GLGizmosManager::EType type = gm.get_current_type(); | ||||
|             if (type == GLGizmosManager::FdmSupports | ||||
|              || type == GLGizmosManager::Seam) { | ||||
|                 || type == GLGizmosManager::Seam | ||||
|                 || type == GLGizmosManager::MmuSegmentation) { | ||||
|                 shader->stop_using(); | ||||
|                 gm.render_painter_gizmo(); | ||||
|                 shader->start_using(); | ||||
|  |  | |||
							
								
								
									
										671
									
								
								src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										671
									
								
								src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,671 @@ | |||
| #include "GLGizmoMmuSegmentation.hpp" | ||||
| 
 | ||||
| #include "slic3r/GUI/GLCanvas3D.hpp" | ||||
| #include "slic3r/GUI/GUI_App.hpp" | ||||
| #include "slic3r/GUI/ImGuiWrapper.hpp" | ||||
| #include "slic3r/GUI/Camera.hpp" | ||||
| #include "slic3r/GUI/Plater.hpp" | ||||
| #include "slic3r/GUI/BitmapCache.hpp" | ||||
| #include "libslic3r/PresetBundle.hpp" | ||||
| #include "libslic3r/Model.hpp" | ||||
| 
 | ||||
| 
 | ||||
| #include <GL/glew.h> | ||||
| 
 | ||||
| namespace Slic3r::GUI { | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::on_shutdown() | ||||
| { | ||||
|     m_parent.use_slope(false); | ||||
| } | ||||
| 
 | ||||
| std::string GLGizmoMmuSegmentation::on_get_name() const | ||||
| { | ||||
|     // FIXME Lukas H.: Discuss and change shortcut
 | ||||
|     return (_L("MMU painting") + " [N]").ToUTF8().data(); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoMmuSegmentation::on_is_selectable() const | ||||
| { | ||||
|     return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF | ||||
|             && wxGetApp().get_mode() != comSimple && wxGetApp().extruders_edited_cnt() > 1); | ||||
| } | ||||
| 
 | ||||
| static std::vector<std::array<uint8_t, 3>> get_extruders_colors() | ||||
| { | ||||
|     unsigned char                       rgb_color[3] = {}; | ||||
|     std::vector<std::string>            colors       = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); | ||||
|     std::vector<std::array<uint8_t, 3>> colors_out(colors.size()); | ||||
|     for (const std::string &color : colors) { | ||||
|         Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); | ||||
|         size_t color_idx      = &color - &colors.front(); | ||||
|         colors_out[color_idx] = {rgb_color[0], rgb_color[1], rgb_color[2]}; | ||||
|     } | ||||
| 
 | ||||
|     return colors_out; | ||||
| } | ||||
| 
 | ||||
| static std::vector<std::string> get_extruders_names() | ||||
| { | ||||
|     size_t                   extruders_count = wxGetApp().extruders_edited_cnt(); | ||||
|     std::vector<std::string> extruders_out; | ||||
|     extruders_out.reserve(extruders_count); | ||||
|     for (size_t extruder_idx = 1; extruder_idx <= extruders_count; ++extruder_idx) | ||||
|         extruders_out.emplace_back("Extruder " + std::to_string(extruder_idx)); | ||||
| 
 | ||||
|     return extruders_out; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::init_extruders_data() | ||||
| { | ||||
|     m_original_extruders_names     = get_extruders_names(); | ||||
|     m_original_extruders_colors    = get_extruders_colors(); | ||||
|     m_modified_extruders_colors    = m_original_extruders_colors; | ||||
|     m_first_selected_extruder_idx  = 0; | ||||
|     m_second_selected_extruder_idx = 1; | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoMmuSegmentation::on_init() | ||||
| { | ||||
|     // FIXME Lukas H.: Discuss and change shortcut
 | ||||
|     m_shortcut_key = WXK_CONTROL_N; | ||||
| 
 | ||||
|     m_desc["reset_direction"]      = _L("Reset direction"); | ||||
|     m_desc["clipping_of_view"]     = _L("Clipping of view") + ": "; | ||||
|     m_desc["cursor_size"]          = _L("Brush size") + ": "; | ||||
|     m_desc["cursor_type"]          = _L("Brush shape") + ": "; | ||||
|     m_desc["first_color_caption"]  = _L("Left mouse button") + ": "; | ||||
|     m_desc["first_color"]          = _L("First color"); | ||||
|     m_desc["second_color_caption"] = _L("Right mouse button") + ": "; | ||||
|     m_desc["second_color"]         = _L("Second color"); | ||||
|     m_desc["remove_caption"]       = _L("Shift + Left mouse button") + ": "; | ||||
|     m_desc["remove"]               = _L("Remove painted color"); | ||||
|     m_desc["remove_all"]           = _L("Remove all painted colors"); | ||||
|     m_desc["circle"]               = _L("Circle"); | ||||
|     m_desc["sphere"]               = _L("Sphere"); | ||||
|     m_desc["seed_fill_angle"]      = _L("Seed fill angle"); | ||||
| 
 | ||||
|     init_extruders_data(); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::render_painter_gizmo() const | ||||
| { | ||||
|     const Selection& selection = m_parent.get_selection(); | ||||
| 
 | ||||
|     glsafe(::glEnable(GL_BLEND)); | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|     render_triangles(selection); | ||||
| 
 | ||||
|     m_c->object_clipper()->render_cut(); | ||||
|     render_cursor(); | ||||
| 
 | ||||
|     glsafe(::glDisable(GL_BLEND)); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoMmuSegmentation::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) | ||||
| { | ||||
|     if (action == SLAGizmoEventType::MouseWheelUp | ||||
|         || action == SLAGizmoEventType::MouseWheelDown) { | ||||
|         if (control_down) { | ||||
|             double pos = m_c->object_clipper()->get_position(); | ||||
|             pos = action == SLAGizmoEventType::MouseWheelDown | ||||
|                   ? std::max(0., pos - 0.01) | ||||
|                   : std::min(1., pos + 0.01); | ||||
|             m_c->object_clipper()->set_position(pos, true); | ||||
|             return true; | ||||
|         } | ||||
|         else if (alt_down) { | ||||
|             m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown | ||||
|                               ? std::max(m_cursor_radius - CursorRadiusStep, CursorRadiusMin) | ||||
|                               : std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax); | ||||
|             m_parent.set_as_dirty(); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::ResetClippingPlane) { | ||||
|         m_c->object_clipper()->set_position(-1., false); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::LeftDown | ||||
|         || action == SLAGizmoEventType::RightDown | ||||
|         || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { | ||||
| 
 | ||||
|         if (m_triangle_selectors.empty()) | ||||
|             return false; | ||||
| 
 | ||||
|         EnforcerBlockerType new_state = EnforcerBlockerType::NONE; | ||||
|         if (! shift_down) { | ||||
|             if (action == SLAGizmoEventType::Dragging) | ||||
|                 new_state = m_button_down == Button::Left | ||||
|                             ? EnforcerBlockerType(m_first_selected_extruder_idx) | ||||
|                             : EnforcerBlockerType(m_second_selected_extruder_idx); | ||||
|             else | ||||
|                 new_state = action == SLAGizmoEventType::LeftDown | ||||
|                             ? EnforcerBlockerType(m_first_selected_extruder_idx) | ||||
|                             : EnforcerBlockerType(m_second_selected_extruder_idx); | ||||
|         } | ||||
| 
 | ||||
|         const Camera& camera = wxGetApp().plater()->get_camera(); | ||||
|         const Selection& selection = m_parent.get_selection(); | ||||
|         const ModelObject* mo = m_c->selection_info()->model_object(); | ||||
|         const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; | ||||
|         const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); | ||||
| 
 | ||||
|         // List of mouse positions that will be used as seeds for painting.
 | ||||
|         std::vector<Vec2d> mouse_positions{mouse_position}; | ||||
| 
 | ||||
|         // In case current mouse position is far from the last one,
 | ||||
|         // add several positions from between into the list, so there
 | ||||
|         // are no gaps in the painted region.
 | ||||
|         { | ||||
|             if (m_last_mouse_click == Vec2d::Zero()) | ||||
|                 m_last_mouse_click = mouse_position; | ||||
|             // resolution describes minimal distance limit using circle radius
 | ||||
|             // as a unit (e.g., 2 would mean the patches will be touching).
 | ||||
|             double resolution = 0.7; | ||||
|             double diameter_px =  resolution  * m_cursor_radius * camera.get_zoom(); | ||||
|             int patches_in_between = int(((mouse_position - m_last_mouse_click).norm() - diameter_px) / diameter_px); | ||||
|             if (patches_in_between > 0) { | ||||
|                 Vec2d diff = (mouse_position - m_last_mouse_click)/(patches_in_between+1); | ||||
|                 for (int i=1; i<=patches_in_between; ++i) | ||||
|                     mouse_positions.emplace_back(m_last_mouse_click + i*diff); | ||||
|             } | ||||
|         } | ||||
|         m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved
 | ||||
| 
 | ||||
|         // Precalculate transformations of individual meshes.
 | ||||
|         std::vector<Transform3d> trafo_matrices; | ||||
|         for (const ModelVolume* mv : mo->volumes) { | ||||
|             if (mv->is_model_part()) | ||||
|                 trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); | ||||
|         } | ||||
| 
 | ||||
|         // Now "click" into all the prepared points and spill paint around them.
 | ||||
|         for (const Vec2d& mp : mouse_positions) { | ||||
|             update_raycast_cache(mp, camera, trafo_matrices); | ||||
| 
 | ||||
|             bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); | ||||
| 
 | ||||
|             // The mouse button click detection is enabled when there is a valid hit.
 | ||||
|             // Missing the object entirely
 | ||||
|             // shall not capture the mouse.
 | ||||
|             if (m_rr.mesh_id != -1) { | ||||
|                 if (m_button_down == Button::None) | ||||
|                     m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); | ||||
|             } | ||||
| 
 | ||||
|             if (m_rr.mesh_id == -1) { | ||||
|                 // In case we have no valid hit, we can return. The event will be stopped when
 | ||||
|                 // dragging while painting (to prevent scene rotations and moving the object)
 | ||||
|                 return dragging_while_painting; | ||||
|             } | ||||
| 
 | ||||
|             const Transform3d& trafo_matrix = trafo_matrices[m_rr.mesh_id]; | ||||
| 
 | ||||
|             // Calculate direction from camera to the hit (in mesh coords):
 | ||||
|             Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>(); | ||||
| 
 | ||||
|             assert(m_rr.mesh_id < int(m_triangle_selectors.size())); | ||||
|             if (m_seed_fill_enabled) | ||||
|                 m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); | ||||
|             else | ||||
|                 m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type, | ||||
|                                                                  new_state, trafo_matrix, m_triangle_splitting_enabled); | ||||
|             m_last_mouse_click = mouse_position; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::Moving && m_seed_fill_enabled) { | ||||
|         if (m_triangle_selectors.empty()) | ||||
|             return false; | ||||
| 
 | ||||
|         const Camera &       camera         = wxGetApp().plater()->get_camera(); | ||||
|         const Selection &    selection      = m_parent.get_selection(); | ||||
|         const ModelObject *  mo             = m_c->selection_info()->model_object(); | ||||
|         const ModelInstance *mi             = mo->instances[selection.get_instance_idx()]; | ||||
|         const Transform3d &  instance_trafo = mi->get_transformation().get_matrix(); | ||||
| 
 | ||||
|         // Precalculate transformations of individual meshes.
 | ||||
|         std::vector<Transform3d> trafo_matrices; | ||||
|         for (const ModelVolume *mv : mo->volumes) | ||||
|             if (mv->is_model_part()) | ||||
|                 trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); | ||||
| 
 | ||||
|         // Now "click" into all the prepared points and spill paint around them.
 | ||||
|         update_raycast_cache(mouse_position, camera, trafo_matrices); | ||||
| 
 | ||||
|         if (m_rr.mesh_id == -1) { | ||||
|             // Clean selected by seed fill for all triangles
 | ||||
|             for (auto &triangle_selector : m_triangle_selectors) | ||||
|                 triangle_selector->seed_fill_unselect_all_triangles(); | ||||
| 
 | ||||
|             // In case we have no valid hit, we can return.
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         assert(m_rr.mesh_id < int(m_triangle_selectors.size())); | ||||
|         m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) | ||||
|         && m_button_down != Button::None) { | ||||
|         // Take snapshot and update ModelVolume data.
 | ||||
|         wxString action_name; | ||||
|         if (get_painter_type() == PainterGizmoType::FDM_SUPPORTS) { | ||||
|             if (shift_down) | ||||
|                 action_name = _L("Remove selection"); | ||||
|             else { | ||||
|                 if (m_button_down == Button::Left) | ||||
|                     action_name = _L("Add supports"); | ||||
|                 else | ||||
|                     action_name = _L("Block supports"); | ||||
|             } | ||||
|         } | ||||
|         if (get_painter_type() == PainterGizmoType::SEAM) { | ||||
|             if (shift_down) | ||||
|                 action_name = _L("Remove selection"); | ||||
|             else { | ||||
|                 if (m_button_down == Button::Left) | ||||
|                     action_name = _L("Enforce seam"); | ||||
|                 else | ||||
|                     action_name = _L("Block seam"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         activate_internal_undo_redo_stack(true); | ||||
|         Plater::TakeSnapshot snapshot(wxGetApp().plater(), action_name); | ||||
|         update_model_object(); | ||||
| 
 | ||||
|         m_button_down = Button::None; | ||||
|         m_last_mouse_click = Vec2d::Zero(); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection) | ||||
| { | ||||
|     GLGizmoPainterBase::set_painter_gizmo_data(selection); | ||||
| 
 | ||||
|     if (m_state != On) | ||||
|         return; | ||||
| 
 | ||||
|     int prev_extruders_count = m_original_extruders_colors.size(); | ||||
|     if (prev_extruders_count != wxGetApp().extruders_edited_cnt() || get_extruders_colors() != m_original_extruders_colors) { | ||||
|         this->init_extruders_data(); | ||||
|         // Reinitialize triangle selectors because of change of extruder count need also change the size of GLIndexedVertexArray
 | ||||
|         if (prev_extruders_count != wxGetApp().extruders_edited_cnt()) | ||||
|             this->init_model_triangle_selectors(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void render_extruders_combo(const std::string                         &label, | ||||
|                                    const std::vector<std::string>            &extruders, | ||||
|                                    const std::vector<std::array<uint8_t, 3>> &extruders_colors, | ||||
|                                    size_t                                    &selection_idx) | ||||
| { | ||||
|     assert(!extruders_colors.empty()); | ||||
|     assert(extruders_colors.size() == extruders_colors.size()); | ||||
| 
 | ||||
|     size_t selection_out = selection_idx; | ||||
| 
 | ||||
|     // It is necessary to use BeginGroup(). Otherwise, when using SameLine() is called, then other items will be drawn inside the combobox.
 | ||||
|     ImGui::BeginGroup(); | ||||
|     ImVec2 combo_pos = ImGui::GetCursorScreenPos(); | ||||
|     if (ImGui::BeginCombo(label.c_str(), "")) { | ||||
|         for (size_t extruder_idx = 0; extruder_idx < extruders.size(); ++extruder_idx) { | ||||
|             ImGui::PushID(int(extruder_idx)); | ||||
|             ImVec2 start_position = ImGui::GetCursorScreenPos(); | ||||
| 
 | ||||
|             if (ImGui::Selectable("", extruder_idx == selection_idx)) | ||||
|                 selection_out = extruder_idx; | ||||
| 
 | ||||
|             ImGui::SameLine(); | ||||
|             ImGuiStyle &style  = ImGui::GetStyle(); | ||||
|             float       height = ImGui::GetTextLineHeight(); | ||||
|             ImGui::GetWindowDrawList()->AddRectFilled(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), | ||||
|                                                       IM_COL32(extruders_colors[extruder_idx][0], extruders_colors[extruder_idx][1], extruders_colors[extruder_idx][2], 255)); | ||||
|             ImGui::GetWindowDrawList()->AddRect(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), IM_COL32_BLACK); | ||||
| 
 | ||||
|             ImGui::SetCursorScreenPos(ImVec2(start_position.x + height + height / 2 + style.FramePadding.x, start_position.y)); | ||||
|             ImGui::Text("%s", extruders[extruder_idx].c_str()); | ||||
|             ImGui::PopID(); | ||||
|         } | ||||
| 
 | ||||
|         ImGui::EndCombo(); | ||||
|     } | ||||
| 
 | ||||
|     ImVec2      backup_pos = ImGui::GetCursorScreenPos(); | ||||
|     ImGuiStyle &style      = ImGui::GetStyle(); | ||||
| 
 | ||||
|     ImGui::SetCursorScreenPos(ImVec2(combo_pos.x + style.FramePadding.x, combo_pos.y + style.FramePadding.y)); | ||||
|     ImVec2 p      = ImGui::GetCursorScreenPos(); | ||||
|     float  height = ImGui::GetTextLineHeight(); | ||||
| 
 | ||||
|     ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + height + height / 2, p.y + height), | ||||
|                                               IM_COL32(extruders_colors[selection_idx][0], extruders_colors[selection_idx][1], | ||||
|                                                        extruders_colors[selection_idx][2], 255)); | ||||
|     ImGui::GetWindowDrawList()->AddRect(p, ImVec2(p.x + height + height / 2, p.y + height), IM_COL32_BLACK); | ||||
| 
 | ||||
|     ImGui::SetCursorScreenPos(ImVec2(p.x + height + height / 2 + style.FramePadding.x, p.y)); | ||||
|     ImGui::Text("%s", extruders[selection_out].c_str()); | ||||
|     ImGui::SetCursorScreenPos(backup_pos); | ||||
|     ImGui::EndGroup(); | ||||
| 
 | ||||
|     selection_idx = selection_out; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bottom_limit) | ||||
| { | ||||
|     if (!m_c->selection_info()->model_object()) | ||||
|         return; | ||||
| 
 | ||||
|     const float approx_height = m_imgui->scaled(23.0f); | ||||
|                             y = std::min(y, bottom_limit - approx_height); | ||||
|     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); | ||||
| 
 | ||||
|     m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); | ||||
| 
 | ||||
|     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
 | ||||
|     const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, | ||||
|                                                 m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); | ||||
|     const float cursor_slider_left       = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); | ||||
|     const float autoset_slider_left      = m_imgui->calc_text_size(m_desc.at("seed_fill_angle")).x + m_imgui->scaled(1.f); | ||||
|     const float cursor_type_radio_left   = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f); | ||||
|     const float cursor_type_radio_width1 = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f); | ||||
|     const float cursor_type_radio_width2 = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f); | ||||
|     const float button_width             = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); | ||||
|     const float buttons_width            = m_imgui->scaled(0.5f); | ||||
|     const float minimal_slider_width     = m_imgui->scaled(4.f); | ||||
|     const float color_button_width       = m_imgui->calc_text_size("").x + m_imgui->scaled(1.75f); | ||||
|     const float combo_label_width        = std::max(m_imgui->calc_text_size(m_desc.at("first_color")).x, | ||||
|                                                     m_imgui->calc_text_size(m_desc.at("second_color")).x) + m_imgui->scaled(1.f); | ||||
| 
 | ||||
|     float caption_max    = 0.f; | ||||
|     float total_text_max = 0.; | ||||
|     for (const std::string &t : {"first_color", "second_color", "remove"}) { | ||||
|         caption_max    = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t + "_caption")).x); | ||||
|         total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); | ||||
|     } | ||||
|     caption_max += m_imgui->scaled(1.f); | ||||
|     total_text_max += m_imgui->scaled(1.f); | ||||
| 
 | ||||
|     float window_width = minimal_slider_width + std::max(autoset_slider_left, std::max(cursor_slider_left, clipping_slider_left)); | ||||
|     window_width       = std::max(window_width, total_text_max); | ||||
|     window_width       = std::max(window_width, button_width); | ||||
|     window_width       = std::max(window_width, cursor_type_radio_left + cursor_type_radio_width1 + cursor_type_radio_width2); | ||||
|     window_width       = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f)); | ||||
| 
 | ||||
|     auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) { | ||||
|         m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption); | ||||
|         ImGui::SameLine(caption_max); | ||||
|         m_imgui->text(text); | ||||
|     }; | ||||
| 
 | ||||
|     for (const std::string &t : {"first_color", "second_color", "remove"}) | ||||
|         draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); | ||||
| 
 | ||||
|     m_imgui->text(""); | ||||
|     ImGui::Separator(); | ||||
| 
 | ||||
|     const std::array<uint8_t, 3> &select_first_color  = m_modified_extruders_colors[m_first_selected_extruder_idx]; | ||||
|     const std::array<uint8_t, 3> &select_second_color = m_modified_extruders_colors[m_second_selected_extruder_idx]; | ||||
| 
 | ||||
|     m_imgui->text(m_desc.at("first_color")); | ||||
|     ImGui::SameLine(combo_label_width); | ||||
|     ImGui::PushItemWidth(window_width - combo_label_width - color_button_width); | ||||
|     render_extruders_combo("##first_color_combo", m_original_extruders_names, m_original_extruders_colors, m_first_selected_extruder_idx); | ||||
|     ImGui::SameLine(); | ||||
| 
 | ||||
|     ImVec4 first_color = ImVec4(float(select_first_color[0]) / 255.0f, float(select_first_color[1]) / 255.0f, float(select_first_color[2]) / 255.0f, 1.0f); | ||||
|     ImVec4 second_color = ImVec4(float(select_second_color[0]) / 255.0f, float(select_second_color[1]) / 255.0f, float(select_second_color[2]) / 255.0f, 1.0f); | ||||
|     if(ImGui::ColorEdit4("First color##color_picker", (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) | ||||
|         m_modified_extruders_colors[m_first_selected_extruder_idx] = {uint8_t(first_color.x * 255.0f), uint8_t(first_color.y * 255.0f), uint8_t(first_color.z * 255.0f)}; | ||||
| 
 | ||||
|     m_imgui->text(m_desc.at("second_color")); | ||||
|     ImGui::SameLine(combo_label_width); | ||||
|     ImGui::PushItemWidth(window_width - combo_label_width - color_button_width); | ||||
|     render_extruders_combo("##second_color_combo", m_original_extruders_names, m_original_extruders_colors, m_second_selected_extruder_idx); | ||||
|     ImGui::SameLine(); | ||||
|     if(ImGui::ColorEdit4("Second color##color_picker", (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) | ||||
|         m_modified_extruders_colors[m_second_selected_extruder_idx] = {uint8_t(second_color.x * 255.0f), uint8_t(second_color.y * 255.0f), uint8_t(second_color.z * 255.0f)}; | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
| 
 | ||||
|     if (m_imgui->checkbox(_L("Seed fill"), m_seed_fill_enabled)) | ||||
|         if (!m_seed_fill_enabled) | ||||
|             for (auto &triangle_selector : m_triangle_selectors) | ||||
|                 triangle_selector->seed_fill_unselect_all_triangles(); | ||||
| 
 | ||||
|     m_imgui->text(m_desc["seed_fill_angle"] + ":"); | ||||
|     ImGui::AlignTextToFramePadding(); | ||||
|     std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Degree sign to use in the respective slider in FDM supports gizmo," | ||||
|                                                                             "placed after the number with no whitespace in between."); | ||||
|     ImGui::SameLine(autoset_slider_left); | ||||
|     ImGui::PushItemWidth(window_width - autoset_slider_left); | ||||
|     m_imgui->disabled_begin(!m_seed_fill_enabled); | ||||
|     m_imgui->slider_float("##seed_fill_angle", &m_seed_fill_angle, 0.f, 90.f, format_str.data()); | ||||
|     m_imgui->disabled_end(); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
| 
 | ||||
|     if (m_imgui->button(m_desc.at("remove_all"))) { | ||||
|         Plater::TakeSnapshot snapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); | ||||
|         ModelObject *mo  = m_c->selection_info()->model_object(); | ||||
|         int          idx = -1; | ||||
|         for (ModelVolume *mv : mo->volumes) { | ||||
|             if (mv->is_model_part()) { | ||||
|                 ++idx; | ||||
|                 size_t extruder_id = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0; | ||||
|                 m_triangle_selectors[idx]->reset(EnforcerBlockerType(extruder_id)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         update_model_object(); | ||||
|         m_parent.set_as_dirty(); | ||||
|     } | ||||
| 
 | ||||
|     const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; | ||||
| 
 | ||||
|     ImGui::AlignTextToFramePadding(); | ||||
|     m_imgui->text(m_desc.at("cursor_size")); | ||||
|     ImGui::SameLine(cursor_slider_left); | ||||
|     ImGui::PushItemWidth(window_width - cursor_slider_left); | ||||
|     ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); | ||||
|     if (ImGui::IsItemHovered()) { | ||||
|         ImGui::BeginTooltip(); | ||||
|         ImGui::PushTextWrapPos(max_tooltip_width); | ||||
|         ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); | ||||
|         ImGui::PopTextWrapPos(); | ||||
|         ImGui::EndTooltip(); | ||||
|     } | ||||
| 
 | ||||
|     ImGui::AlignTextToFramePadding(); | ||||
|     m_imgui->text(m_desc.at("cursor_type")); | ||||
|     ImGui::SameLine(cursor_type_radio_left + m_imgui->scaled(0.f)); | ||||
|     ImGui::PushItemWidth(cursor_type_radio_width1); | ||||
| 
 | ||||
|     bool sphere_sel = m_cursor_type == TriangleSelector::CursorType::SPHERE; | ||||
|     if (m_imgui->radio_button(m_desc["sphere"], sphere_sel)) | ||||
|         sphere_sel = true; | ||||
| 
 | ||||
|     if (ImGui::IsItemHovered()) { | ||||
|         ImGui::BeginTooltip(); | ||||
|         ImGui::PushTextWrapPos(max_tooltip_width); | ||||
|         ImGui::TextUnformatted(_L("Paints all facets inside, regardless of their orientation.").ToUTF8().data()); | ||||
|         ImGui::PopTextWrapPos(); | ||||
|         ImGui::EndTooltip(); | ||||
|     } | ||||
| 
 | ||||
|     ImGui::SameLine(cursor_type_radio_left + cursor_type_radio_width2 + m_imgui->scaled(0.f)); | ||||
|     ImGui::PushItemWidth(cursor_type_radio_width2); | ||||
| 
 | ||||
|     if (m_imgui->radio_button(m_desc["circle"], !sphere_sel)) | ||||
|         sphere_sel = false; | ||||
| 
 | ||||
|     if (ImGui::IsItemHovered()) { | ||||
|         ImGui::BeginTooltip(); | ||||
|         ImGui::PushTextWrapPos(max_tooltip_width); | ||||
|         ImGui::TextUnformatted(_L("Ignores facets facing away from the camera.").ToUTF8().data()); | ||||
|         ImGui::PopTextWrapPos(); | ||||
|         ImGui::EndTooltip(); | ||||
|     } | ||||
| 
 | ||||
|     m_cursor_type = sphere_sel ? TriangleSelector::CursorType::SPHERE : TriangleSelector::CursorType::CIRCLE; | ||||
| 
 | ||||
|     m_imgui->checkbox(_L("Split triangles"), m_triangle_splitting_enabled); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|     if (m_c->object_clipper()->get_position() == 0.f) { | ||||
|         ImGui::AlignTextToFramePadding(); | ||||
|         m_imgui->text(m_desc.at("clipping_of_view")); | ||||
|     } else { | ||||
|         if (m_imgui->button(m_desc.at("reset_direction"))) { | ||||
|             wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position(-1., false); }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ImGui::SameLine(clipping_slider_left); | ||||
|     ImGui::PushItemWidth(window_width - clipping_slider_left); | ||||
|     auto clp_dist = float(m_c->object_clipper()->get_position()); | ||||
|     if (ImGui::SliderFloat("  ", &clp_dist, 0.f, 1.f, "%.2f")) | ||||
|         m_c->object_clipper()->set_position(clp_dist, true); | ||||
|     if (ImGui::IsItemHovered()) { | ||||
|         ImGui::BeginTooltip(); | ||||
|         ImGui::PushTextWrapPos(max_tooltip_width); | ||||
|         ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); | ||||
|         ImGui::PopTextWrapPos(); | ||||
|         ImGui::EndTooltip(); | ||||
|     } | ||||
|     m_imgui->end(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::update_model_object() const | ||||
| { | ||||
|     bool updated = false; | ||||
|     ModelObject* mo = m_c->selection_info()->model_object(); | ||||
|     int idx = -1; | ||||
|     for (ModelVolume* mv : mo->volumes) { | ||||
|         if (! mv->is_model_part()) | ||||
|             continue; | ||||
|         ++idx; | ||||
|         updated |= mv->mmu_segmentation_facets.set(*m_triangle_selectors[idx].get()); | ||||
|     } | ||||
| 
 | ||||
|     if (updated) | ||||
|         m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::init_model_triangle_selectors() | ||||
| { | ||||
|     const ModelObject *mo = m_c->selection_info()->model_object(); | ||||
|     m_triangle_selectors.clear(); | ||||
| 
 | ||||
|     for (const ModelVolume *mv : mo->volumes) { | ||||
|         if (!mv->is_model_part()) | ||||
|             continue; | ||||
| 
 | ||||
|         // This mesh does not account for the possible Z up SLA offset.
 | ||||
|         const TriangleMesh *mesh = &mv->mesh(); | ||||
| 
 | ||||
|         size_t extruder_id = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0; | ||||
|         m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorMmuGui>(*mesh, m_modified_extruders_colors)); | ||||
|         m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data(), EnforcerBlockerType(extruder_id)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMmuSegmentation::update_from_model_object() | ||||
| { | ||||
|     wxBusyCursor wait; | ||||
|     this->init_model_triangle_selectors(); | ||||
| } | ||||
| 
 | ||||
| PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const | ||||
| { | ||||
|     return PainterGizmoType::MMU_SEGMENTATION; | ||||
| } | ||||
| 
 | ||||
| void TriangleSelectorMmuGui::render(ImGuiWrapper *imgui) | ||||
| { | ||||
|     std::vector<int> color_cnt(m_iva_colors.size()); | ||||
|     int              seed_fill_cnt = 0; | ||||
|     for (auto &iva_color : m_iva_colors) | ||||
|         iva_color.release_geometry(); | ||||
|     m_iva_seed_fill.release_geometry(); | ||||
| 
 | ||||
|     for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) { | ||||
|         for (const Triangle &tr : m_triangles) { | ||||
|             if (!tr.valid || tr.is_split() || tr.is_selected_by_seed_fill() || tr.get_state() != EnforcerBlockerType(color_idx)) | ||||
|                 continue; | ||||
| 
 | ||||
|             for (int i = 0; i < 3; ++i) | ||||
|                 m_iva_colors[color_idx].push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), | ||||
|                                                       double(m_vertices[tr.verts_idxs[i]].v[1]), | ||||
|                                                       double(m_vertices[tr.verts_idxs[i]].v[2]), | ||||
|                                                       double(tr.normal[0]), | ||||
|                                                       double(tr.normal[1]), | ||||
|                                                       double(tr.normal[2])); | ||||
|             m_iva_colors[color_idx].push_triangle(color_cnt[color_idx], color_cnt[color_idx] + 1, color_cnt[color_idx] + 2); | ||||
|             color_cnt[color_idx] += 3; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (const Triangle &tr : m_triangles) { | ||||
|         if (!tr.valid || tr.is_split() || !tr.is_selected_by_seed_fill()) continue; | ||||
| 
 | ||||
|         for (int i = 0; i < 3; ++i) | ||||
|             m_iva_seed_fill.push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), | ||||
|                                           double(m_vertices[tr.verts_idxs[i]].v[1]), | ||||
|                                           double(m_vertices[tr.verts_idxs[i]].v[2]), | ||||
|                                           double(tr.normal[0]), | ||||
|                                           double(tr.normal[1]), | ||||
|                                           double(tr.normal[2])); | ||||
|         m_iva_seed_fill.push_triangle(seed_fill_cnt, seed_fill_cnt + 1, seed_fill_cnt + 2); | ||||
|         seed_fill_cnt += 3; | ||||
|     } | ||||
| 
 | ||||
|     for (auto &iva_color : m_iva_colors) | ||||
|         iva_color.finalize_geometry(true); | ||||
|     m_iva_seed_fill.finalize_geometry(true); | ||||
| 
 | ||||
|     std::vector<bool> render_colors(m_iva_colors.size()); | ||||
|     for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) | ||||
|         render_colors[color_idx] = m_iva_colors[color_idx].has_VBOs(); | ||||
|     bool render_seed_fill = m_iva_seed_fill.has_VBOs(); | ||||
| 
 | ||||
|     auto *shader = wxGetApp().get_shader("gouraud"); | ||||
|     if (!shader) return; | ||||
| 
 | ||||
|     shader->start_using(); | ||||
|     ScopeGuard guard([shader]() { | ||||
|         if (shader) | ||||
|             shader->stop_using(); | ||||
|     }); | ||||
|     shader->set_uniform("slope.actived", false); | ||||
| 
 | ||||
|     for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) { | ||||
|         if (render_colors[color_idx]) { | ||||
|             std::array<float, 4> color = {float(m_colors[color_idx][0]) / 255.0f, float(m_colors[color_idx][1]) / 255.0f, | ||||
|                                           float(m_colors[color_idx][2]) / 255.0f, 1.f}; | ||||
|             shader->set_uniform("uniform_color", color); | ||||
|             m_iva_colors[color_idx].render(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (render_seed_fill) { | ||||
|         std::array<float, 4> color = {0.f, 1.f, 0.44f, 1.f}; | ||||
|         shader->set_uniform("uniform_color", color); | ||||
|         m_iva_seed_fill.render(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
							
								
								
									
										69
									
								
								src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | |||
| #ifndef slic3r_GLGizmoMmuSegmentation_hpp_ | ||||
| #define slic3r_GLGizmoMmuSegmentation_hpp_ | ||||
| 
 | ||||
| #include "GLGizmoPainterBase.hpp" | ||||
| 
 | ||||
| namespace Slic3r::GUI { | ||||
| 
 | ||||
| class TriangleSelectorMmuGui : public TriangleSelectorGUI { | ||||
| public: | ||||
|     explicit TriangleSelectorMmuGui(const TriangleMesh& mesh, const std::vector<std::array<uint8_t, 3>> &colors) | ||||
|         : TriangleSelectorGUI(mesh), m_colors(colors) { | ||||
|         m_iva_colors = std::vector<GLIndexedVertexArray>(colors.size()); | ||||
|     } | ||||
| 
 | ||||
|     // Render current selection. Transformation matrices are supposed
 | ||||
|     // to be already set.
 | ||||
|     void render(ImGuiWrapper* imgui) override; | ||||
| 
 | ||||
| private: | ||||
|     const std::vector<std::array<uint8_t, 3>> &m_colors; | ||||
|     std::vector<GLIndexedVertexArray> m_iva_colors; | ||||
| }; | ||||
| 
 | ||||
| class GLGizmoMmuSegmentation : public GLGizmoPainterBase | ||||
| { | ||||
| public: | ||||
|     GLGizmoMmuSegmentation(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||
|         : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} | ||||
| 
 | ||||
|     void render_painter_gizmo() const override; | ||||
| 
 | ||||
|     bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; | ||||
| 
 | ||||
|     virtual void set_painter_gizmo_data(const Selection& selection) override; | ||||
| 
 | ||||
| protected: | ||||
|     void on_render_input_window(float x, float y, float bottom_limit) override; | ||||
|     std::string on_get_name() const override; | ||||
| 
 | ||||
|     bool on_is_selectable() const override; | ||||
| 
 | ||||
|     size_t                              m_first_selected_extruder_idx  = 0; | ||||
|     size_t                              m_second_selected_extruder_idx = 1; | ||||
|     std::vector<std::string>            m_original_extruders_names; | ||||
|     std::vector<std::array<uint8_t, 3>> m_original_extruders_colors; | ||||
|     std::vector<std::array<uint8_t, 3>> m_modified_extruders_colors; | ||||
| 
 | ||||
| private: | ||||
|     bool on_init() override; | ||||
| 
 | ||||
|     void update_model_object() const override; | ||||
|     void update_from_model_object() override; | ||||
| 
 | ||||
|     void on_opening() override {} | ||||
|     void on_shutdown() override; | ||||
|     PainterGizmoType get_painter_type() const override; | ||||
| 
 | ||||
|     void init_model_triangle_selectors(); | ||||
|     void init_extruders_data(); | ||||
| 
 | ||||
|     // This map holds all translated description texts, so they can be easily referenced during layout calculations
 | ||||
|     // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
 | ||||
|     std::map<std::string, wxString> m_desc; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| 
 | ||||
| #endif // slic3r_GLGizmoMmuSegmentation_hpp_
 | ||||
|  | @ -73,7 +73,7 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) | |||
|     if (activate && ! m_internal_stack_active) { | ||||
|         wxString str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS | ||||
|                            ? _L("Entering Paint-on supports") | ||||
|                            : _L("Entering Seam painting"); | ||||
|                            : (get_painter_type() == PainterGizmoType::MMU_SEGMENTATION ? _L("Entering MMU segmentation") : _L("Entering Seam painting")); | ||||
|         Plater::TakeSnapshot(wxGetApp().plater(), str); | ||||
|         wxGetApp().plater()->enter_gizmos_stack(); | ||||
|         m_internal_stack_active = true; | ||||
|  | @ -81,7 +81,7 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) | |||
|     if (! activate && m_internal_stack_active) { | ||||
|         wxString str = get_painter_type() == PainterGizmoType::SEAM | ||||
|                            ? _L("Leaving Seam painting") | ||||
|                            : _L("Leaving Paint-on supports"); | ||||
|                            : (get_painter_type() == PainterGizmoType::MMU_SEGMENTATION ? _L("Leaving MMU segmentation") : _L("Leaving Paint-on supports")); | ||||
|         wxGetApp().plater()->leave_gizmos_stack(); | ||||
|         Plater::TakeSnapshot(wxGetApp().plater(), str); | ||||
|         m_internal_stack_active = false; | ||||
|  | @ -180,11 +180,12 @@ void GLGizmoPainterBase::render_cursor() const | |||
|     if (m_rr.mesh_id == -1) | ||||
|         return; | ||||
| 
 | ||||
| 
 | ||||
|     if (m_cursor_type == TriangleSelector::SPHERE) | ||||
|         render_cursor_sphere(trafo_matrices[m_rr.mesh_id]); | ||||
|     else | ||||
|         render_cursor_circle(); | ||||
|     if (!m_seed_fill_enabled) { | ||||
|         if (m_cursor_type == TriangleSelector::SPHERE) | ||||
|             render_cursor_sphere(trafo_matrices[m_rr.mesh_id]); | ||||
|         else | ||||
|             render_cursor_circle(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -388,14 +389,50 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous | |||
|             Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>(); | ||||
| 
 | ||||
|             assert(m_rr.mesh_id < int(m_triangle_selectors.size())); | ||||
|             m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, | ||||
|                                        m_cursor_radius, m_cursor_type, new_state, trafo_matrix); | ||||
|             if (m_seed_fill_enabled) | ||||
|                 m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); | ||||
|             else | ||||
|                 m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type, | ||||
|                                                                  new_state, trafo_matrix, m_triangle_splitting_enabled); | ||||
|             m_last_mouse_click = mouse_position; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::Moving && m_seed_fill_enabled) { | ||||
|         if (m_triangle_selectors.empty()) | ||||
|             return false; | ||||
| 
 | ||||
|         const Camera &       camera         = wxGetApp().plater()->get_camera(); | ||||
|         const Selection &    selection      = m_parent.get_selection(); | ||||
|         const ModelObject *  mo             = m_c->selection_info()->model_object(); | ||||
|         const ModelInstance *mi             = mo->instances[selection.get_instance_idx()]; | ||||
|         const Transform3d &  instance_trafo = mi->get_transformation().get_matrix(); | ||||
| 
 | ||||
|         // Precalculate transformations of individual meshes.
 | ||||
|         std::vector<Transform3d> trafo_matrices; | ||||
|         for (const ModelVolume *mv : mo->volumes) | ||||
|             if (mv->is_model_part()) | ||||
|                 trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); | ||||
| 
 | ||||
|         // Now "click" into all the prepared points and spill paint around them.
 | ||||
|         update_raycast_cache(mouse_position, camera, trafo_matrices); | ||||
| 
 | ||||
|         if (m_rr.mesh_id == -1) { | ||||
|             // Clean selected by seed fill for all triangles
 | ||||
|             for (auto &triangle_selector : m_triangle_selectors) | ||||
|                 triangle_selector->seed_fill_unselect_all_triangles(); | ||||
| 
 | ||||
|             // In case we have no valid hit, we can return.
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         assert(m_rr.mesh_id < int(m_triangle_selectors.size())); | ||||
|         m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) | ||||
|       && m_button_down != Button::None) { | ||||
|         // Take snapshot and update ModelVolume data.
 | ||||
|  | @ -558,12 +595,14 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) | |||
| { | ||||
|     int enf_cnt = 0; | ||||
|     int blc_cnt = 0; | ||||
|     int seed_fill_cnt = 0; | ||||
| 
 | ||||
|     m_iva_enforcers.release_geometry(); | ||||
|     m_iva_blockers.release_geometry(); | ||||
|     m_iva_seed_fill.release_geometry(); | ||||
| 
 | ||||
|     for (const Triangle& tr : m_triangles) { | ||||
|         if (! tr.valid || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE) | ||||
|         if (!tr.valid || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE || tr.is_selected_by_seed_fill()) | ||||
|             continue; | ||||
| 
 | ||||
|         GLIndexedVertexArray& va = tr.get_state() == EnforcerBlockerType::ENFORCER | ||||
|  | @ -580,17 +619,32 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) | |||
|                              double(tr.normal[0]), | ||||
|                              double(tr.normal[1]), | ||||
|                              double(tr.normal[2])); | ||||
|         va.push_triangle(cnt, | ||||
|                          cnt+1, | ||||
|                          cnt+2); | ||||
|         va.push_triangle(cnt, cnt + 1, cnt + 2); | ||||
|         cnt += 3; | ||||
|     } | ||||
| 
 | ||||
|     for (const Triangle &tr : m_triangles) { | ||||
|         if (!tr.valid || tr.is_split() || !tr.is_selected_by_seed_fill()) | ||||
|             continue; | ||||
| 
 | ||||
|         for (int i = 0; i < 3; ++i) | ||||
|             m_iva_seed_fill.push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), | ||||
|                                           double(m_vertices[tr.verts_idxs[i]].v[1]), | ||||
|                                           double(m_vertices[tr.verts_idxs[i]].v[2]), | ||||
|                                           double(tr.normal[0]), | ||||
|                                           double(tr.normal[1]), | ||||
|                                           double(tr.normal[2])); | ||||
|         m_iva_seed_fill.push_triangle(seed_fill_cnt, seed_fill_cnt + 1, seed_fill_cnt + 2); | ||||
|         seed_fill_cnt += 3; | ||||
|     } | ||||
| 
 | ||||
|     m_iva_enforcers.finalize_geometry(true); | ||||
|     m_iva_blockers.finalize_geometry(true); | ||||
|     m_iva_seed_fill.finalize_geometry(true); | ||||
| 
 | ||||
|     bool render_enf = m_iva_enforcers.has_VBOs(); | ||||
|     bool render_blc = m_iva_blockers.has_VBOs(); | ||||
|     bool render_seed_fill = m_iva_seed_fill.has_VBOs(); | ||||
| 
 | ||||
|     auto* shader = wxGetApp().get_shader("gouraud"); | ||||
|     if (! shader) | ||||
|  | @ -612,6 +666,12 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) | |||
|         m_iva_blockers.render(); | ||||
|     } | ||||
| 
 | ||||
|     if (render_seed_fill) { | ||||
|         std::array<float, 4> color = { 0.f, 1.00f, 0.44f, 1.f }; | ||||
|         shader->set_uniform("uniform_color", color); | ||||
|         m_iva_seed_fill.render(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG | ||||
|     if (imgui) | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| enum class EnforcerBlockerType : int8_t; | ||||
|  | @ -22,10 +21,12 @@ namespace GUI { | |||
| enum class SLAGizmoEventType : unsigned char; | ||||
| class ClippingPlane; | ||||
| struct Camera; | ||||
| class GLGizmoMmuSegmentation; | ||||
| 
 | ||||
| enum class PainterGizmoType { | ||||
|     FDM_SUPPORTS, | ||||
|     SEAM | ||||
|     SEAM, | ||||
|     MMU_SEGMENTATION | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -36,7 +37,7 @@ public: | |||
| 
 | ||||
|     // Render current selection. Transformation matrices are supposed
 | ||||
|     // to be already set.
 | ||||
|     void render(ImGuiWrapper* imgui = nullptr); | ||||
|     virtual void render(ImGuiWrapper* imgui = nullptr); | ||||
| 
 | ||||
| #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG | ||||
|     void render_debug(ImGuiWrapper* imgui); | ||||
|  | @ -48,6 +49,8 @@ private: | |||
|     GLIndexedVertexArray m_iva_enforcers; | ||||
|     GLIndexedVertexArray m_iva_blockers; | ||||
|     std::array<GLIndexedVertexArray, 3> m_varrays; | ||||
| protected: | ||||
|     GLIndexedVertexArray m_iva_seed_fill; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -65,8 +68,8 @@ private: | |||
| public: | ||||
|     GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||
|     ~GLGizmoPainterBase() override {} | ||||
|     void set_painter_gizmo_data(const Selection& selection); | ||||
|     bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); | ||||
|     virtual void set_painter_gizmo_data(const Selection& selection); | ||||
|     virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); | ||||
| 
 | ||||
|     // Following function renders the triangles and cursor. Having this separated
 | ||||
|     // from usual on_render method allows to render them before transparent objects,
 | ||||
|  | @ -94,6 +97,9 @@ protected: | |||
| 
 | ||||
|     TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE; | ||||
| 
 | ||||
|     bool  m_triangle_splitting_enabled = true; | ||||
|     bool  m_seed_fill_enabled          = false; | ||||
|     float m_seed_fill_angle            = 0.f; | ||||
| 
 | ||||
| private: | ||||
|     bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const; | ||||
|  | @ -141,6 +147,8 @@ protected: | |||
|     void on_load(cereal::BinaryInputArchive& ar) override; | ||||
|     void on_save(cereal::BinaryOutputArchive& ar) const override {} | ||||
|     CommonGizmosDataID on_get_requirements() const override; | ||||
| 
 | ||||
|     friend class ::Slic3r::GUI::GLGizmoMmuSegmentation; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -110,7 +110,7 @@ void GLGizmoRotate::on_update(const UpdateData& data) | |||
|     Vec2d orig_dir = Vec2d::UnitX(); | ||||
|     Vec2d new_dir = mouse_pos.normalized(); | ||||
| 
 | ||||
|     double theta = ::acos(clamp(-1.0, 1.0, new_dir.dot(orig_dir))); | ||||
|     double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); | ||||
|     if (cross2(orig_dir, new_dir) < 0.0) | ||||
|         theta = 2.0 * (double)PI - theta; | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ enum class SLAGizmoEventType : unsigned char { | |||
| #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,7 +32,8 @@ enum class SLAGizmoEventType : unsigned char { | |||
|     ManualEditing, | ||||
|     MouseWheelUp, | ||||
|     MouseWheelDown, | ||||
|     ResetClippingPlane | ||||
|     ResetClippingPlane, | ||||
|     Moving | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ | |||
| #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" | ||||
| 
 | ||||
| #include "libslic3r/Model.hpp" | ||||
| #include "libslic3r/PresetBundle.hpp" | ||||
|  | @ -108,6 +109,7 @@ bool GLGizmosManager::init() | |||
|     m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6)); | ||||
|     m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7)); | ||||
|     m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); | ||||
|     m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "fdm_supports.svg", 9)); | ||||
| 
 | ||||
|     m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); | ||||
| 
 | ||||
|  | @ -401,6 +403,7 @@ void GLGizmosManager::set_painter_gizmo_data() | |||
| 
 | ||||
|     dynamic_cast<GLGizmoFdmSupports*>(m_gizmos[FdmSupports].get())->set_painter_gizmo_data(m_parent.get_selection()); | ||||
|     dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->set_painter_gizmo_data(m_parent.get_selection()); | ||||
|     dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->set_painter_gizmo_data(m_parent.get_selection()); | ||||
| } | ||||
| 
 | ||||
| // Returns true if the gizmo used the event to do something, false otherwise.
 | ||||
|  | @ -417,6 +420,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p | |||
|         return dynamic_cast<GLGizmoFdmSupports*>(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); | ||||
|     else if (m_current == Seam) | ||||
|         return dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); | ||||
|     else if (m_current == MmuSegmentation) | ||||
|         return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); | ||||
|     else | ||||
|         return false; | ||||
| } | ||||
|  | @ -493,7 +498,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) | |||
| { | ||||
|     bool processed = false; | ||||
| 
 | ||||
|     if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) { | ||||
|     if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) { | ||||
|         float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); | ||||
|         if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) | ||||
|             processed = true; | ||||
|  | @ -518,9 +523,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|     bool control_down = evt.CmdDown(); | ||||
| 
 | ||||
|     // mouse anywhere
 | ||||
|     if (evt.Moving()) | ||||
|     if (evt.Moving()) { | ||||
|         m_tooltip = update_hover_state(mouse_pos); | ||||
|     else if (evt.LeftUp()) { | ||||
|         if (m_current == MmuSegmentation) | ||||
|             gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown()); | ||||
|     } else if (evt.LeftUp()) { | ||||
|         if (m_mouse_capture.left) { | ||||
|             processed = true; | ||||
|             m_mouse_capture.left = false; | ||||
|  | @ -626,7 +633,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|         m_tooltip = ""; | ||||
| 
 | ||||
|         if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { | ||||
|             if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) | ||||
|             if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) | ||||
|                 && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) | ||||
|                 // the gizmo got the event and took some action, there is no need to do anything more
 | ||||
|                 processed = true; | ||||
|  | @ -652,16 +659,16 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|             // event was taken care of by the SlaSupports gizmo
 | ||||
|             processed = true; | ||||
|         } | ||||
|         else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (m_current == FdmSupports || m_current == Seam) | ||||
|         else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) | ||||
|             && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { | ||||
|             // event was taken care of by the FdmSupports / Seam gizmo
 | ||||
|             // event was taken care of by the FdmSupports / Seam / MMUPainting gizmo
 | ||||
|             processed = true; | ||||
|         } | ||||
|         else if (evt.Dragging() && m_parent.get_move_volume_id() != -1 | ||||
|              && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam)) | ||||
|             && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)) | ||||
|             // don't allow dragging objects with the Sla gizmo on
 | ||||
|             processed = true; | ||||
|         else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) | ||||
|         else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam  || m_current == MmuSegmentation) | ||||
|             && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) { | ||||
|             // the gizmo got the event and took some action, no need to do anything more here
 | ||||
|             m_parent.set_as_dirty(); | ||||
|  | @ -674,7 +681,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|             else if (evt.RightIsDown()) | ||||
|                 gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); | ||||
|         } | ||||
|         else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) { | ||||
|         else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) { | ||||
|             // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither
 | ||||
|             // object moving or selecting is suppressed in that case
 | ||||
|             gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); | ||||
|  | @ -684,7 +691,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|             // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
 | ||||
|             processed = true; | ||||
|         } | ||||
|         else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) { | ||||
|         else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) { | ||||
|             gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); | ||||
|             processed = true; | ||||
|         } | ||||
|  | @ -768,7 +775,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) | |||
|         case 'r' : | ||||
|         case 'R' : | ||||
|         { | ||||
|             if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) | ||||
|             if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) | ||||
|                 processed = true; | ||||
| 
 | ||||
|             break; | ||||
|  |  | |||
|  | @ -68,6 +68,7 @@ public: | |||
|         SlaSupports, | ||||
|         FdmSupports, | ||||
|         Seam, | ||||
|         MmuSegmentation, | ||||
|         Undefined | ||||
|     }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -3273,6 +3273,7 @@ void Plater::priv::reload_from_disk() | |||
|                         new_volume->convert_from_meters(); | ||||
|                     new_volume->supported_facets.assign(old_volume->supported_facets); | ||||
|                     new_volume->seam_facets.assign(old_volume->seam_facets); | ||||
|                     new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); | ||||
|                     std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back()); | ||||
|                     old_model_object->delete_volume(old_model_object->volumes.size() - 1); | ||||
| #if ENABLE_ALLOW_NEGATIVE_Z | ||||
|  | @ -3345,13 +3346,14 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = | |||
| 
 | ||||
|     ModelObject* mo = model.objects[obj_idx]; | ||||
| 
 | ||||
|     // If there are custom supports/seams, remove them. Fixed mesh
 | ||||
|     // If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh
 | ||||
|     // may be different and they would make no sense.
 | ||||
|     bool paint_removed = false; | ||||
|     for (ModelVolume* mv : mo->volumes) { | ||||
|         paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty(); | ||||
|         paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty(); | ||||
|         mv->supported_facets.clear(); | ||||
|         mv->seam_facets.clear(); | ||||
|         mv->mmu_segmentation_facets.clear(); | ||||
|     } | ||||
|     if (paint_removed) { | ||||
|         // snapshot_time is captured by copy so the lambda knows where to undo/redo to.
 | ||||
|  |  | |||
|  | @ -1589,6 +1589,7 @@ void TabPrint::build() | |||
| 
 | ||||
|         optgroup = page->new_optgroup(L("Advanced")); | ||||
|         optgroup->append_single_option_line("interface_shells"); | ||||
|         optgroup->append_single_option_line("mmu_segmented_region_max_width"); | ||||
| 
 | ||||
|     page = add_options_page(L("Advanced"), "wrench"); | ||||
|         optgroup = page->new_optgroup(L("Extrusion width")); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vojtech Bubnik
						Vojtech Bubnik