diff --git a/resources/icons/seam.svg b/resources/icons/seam.svg
new file mode 100644
index 0000000000..119fb6afcc
--- /dev/null
+++ b/resources/icons/seam.svg
@@ -0,0 +1,42 @@
+
+
+
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index f1089ae935..33994fe8ec 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -53,6 +53,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoHollow.hpp
GUI/Gizmos/GLGizmoPainterBase.cpp
GUI/Gizmos/GLGizmoPainterBase.hpp
+ GUI/Gizmos/GLGizmoSeam.cpp
+ GUI/Gizmos/GLGizmoSeam.hpp
GUI/GLSelectionRectangle.cpp
GUI/GLSelectionRectangle.hpp
GUI/GLTexture.hpp
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 04ce89a80e..6646a12579 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3582,7 +3582,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::FdmSupports
+ && m_gizmos.get_current_type() != GLGizmosManager::Seam)
{
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
m_dirty = true;
@@ -5317,7 +5318,8 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const
bool show_texture = ! bottom ||
(m_gizmos.get_current_type() != GLGizmosManager::FdmSupports
- && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports);
+ && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports
+ && m_gizmos.get_current_type() != GLGizmosManager::Seam);
wxGetApp().plater()->get_bed().render(const_cast(*this), bottom, scale_factor, show_axes, show_texture);
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
index 3ab58c2585..44f0a69729 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
@@ -124,7 +124,6 @@ public:
void set_state(EState state) { m_state = state; on_set_state(); }
int get_shortcut_key() const { return m_shortcut_key; }
- void set_shortcut_key(int key) { m_shortcut_key = key; }
const std::string& get_icon_filename() const { return m_icon_filename; }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index cc08f86a73..a34eca1a66 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -18,13 +18,6 @@ namespace GUI {
-void GLGizmoFdmSupports::on_opening()
-{
-
-}
-
-
-
void GLGizmoFdmSupports::on_shutdown()
{
if (m_setting_angle) {
@@ -35,6 +28,13 @@ void GLGizmoFdmSupports::on_shutdown()
+std::string GLGizmoFdmSupports::on_get_name() const
+{
+ return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data();
+}
+
+
+
bool GLGizmoFdmSupports::on_init()
{
m_shortcut_key = WXK_CONTROL_L;
@@ -176,12 +176,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
}
m_imgui->end();
- if (m_setting_angle) {
- m_parent.show_slope(false);
- m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg});
- m_parent.use_slope(true);
- m_parent.set_as_dirty();
- }
}
else {
std::string name = "Autoset custom supports";
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
index dc0788c2c8..7100d611e6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
@@ -15,6 +15,7 @@ public:
protected:
void on_render_input_window(float x, float y, float bottom_limit) override;
+ std::string on_get_name() const override;
private:
bool on_init() override;
@@ -24,7 +25,7 @@ private:
void update_model_object() const override;
void update_from_model_object() override;
- void on_opening() override;
+ void on_opening() override {}
void on_shutdown() override;
void select_facets_by_angle(float threshold, bool block);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
index 365d71316c..1809b417cc 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@@ -375,11 +375,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
-
-
-
-
-
bool GLGizmoPainterBase::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
@@ -403,11 +398,6 @@ bool GLGizmoPainterBase::on_is_selectable() const
&& wxGetApp().get_mode() != comSimple );
}
-std::string GLGizmoPainterBase::on_get_name() const
-{
- return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data();
-}
-
CommonGizmosDataID GLGizmoPainterBase::on_get_requirements() const
{
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
index 886807b6a5..da9b378957 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
@@ -104,7 +104,6 @@ protected:
virtual void on_opening() = 0;
virtual void on_shutdown() = 0;
- std::string on_get_name() const override;
bool on_is_activable() const override;
bool on_is_selectable() const override;
void on_load(cereal::BinaryInputArchive& ar) override;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
new file mode 100644
index 0000000000..8a08f5ebe7
--- /dev/null
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
@@ -0,0 +1,208 @@
+#include "GLGizmoSeam.hpp"
+
+#include "libslic3r/Model.hpp"
+
+//#include "slic3r/GUI/3DScene.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
+#include "slic3r/GUI/GUI_App.hpp"
+#include "slic3r/GUI/ImGuiWrapper.hpp"
+#include "slic3r/GUI/Plater.hpp"
+
+
+#include
+
+
+namespace Slic3r {
+
+namespace GUI {
+
+
+
+bool GLGizmoSeam::on_init()
+{
+ m_shortcut_key = WXK_CONTROL_P;
+
+ m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
+ m_desc["reset_direction"] = _L("Reset direction");
+ m_desc["cursor_size"] = _L("Cursor size") + ": ";
+ m_desc["enforce_caption"] = _L("Left mouse button") + ": ";
+ m_desc["enforce"] = _L("Enforce seam");
+ m_desc["block_caption"] = _L("Right mouse button") + " ";
+ m_desc["block"] = _L("Block seam");
+ m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": ";
+ m_desc["remove"] = _L("Remove selection");
+ m_desc["remove_all"] = _L("Remove all selection");
+
+ return true;
+}
+
+
+
+std::string GLGizmoSeam::on_get_name() const
+{
+ return (_(L("Seam Editing")) + " [P]").ToUTF8().data();
+}
+
+
+
+void GLGizmoSeam::on_render() 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_circle();
+
+ glsafe(::glDisable(GL_BLEND));
+}
+
+
+
+void GLGizmoSeam::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(18.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 button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
+ const float minimal_slider_width = m_imgui->scaled(4.f);
+
+ float caption_max = 0.f;
+ float total_text_max = 0.;
+ for (const std::string& t : {"enforce", "block", "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(cursor_slider_left, clipping_slider_left);
+ window_width = std::max(window_width, total_text_max);
+ window_width = std::max(window_width, button_width);
+
+ auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
+ static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
+ m_imgui->text_colored(ORANGE, caption);
+ ImGui::SameLine(caption_max);
+ m_imgui->text(text);
+ };
+
+ for (const std::string& t : {"enforce", "block", "remove"})
+ draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
+
+ m_imgui->text("");
+
+ if (m_imgui->button(m_desc.at("remove_all"))) {
+ Plater::TakeSnapshot(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;
+ m_triangle_selectors[idx]->reset();
+ }
+ }
+
+ update_model_object();
+ m_parent.set_as_dirty();
+ }
+
+ const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
+
+ m_imgui->text(m_desc.at("cursor_size"));
+ ImGui::SameLine(clipping_slider_left);
+ ImGui::PushItemWidth(window_width - clipping_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::Separator();
+ if (m_c->object_clipper()->get_position() == 0.f)
+ m_imgui->text(m_desc.at("clipping_of_view"));
+ else {
+ if (m_imgui->button(m_desc.at("reset_direction"))) {
+ wxGetApp().CallAfter([this](){
+ m_c->object_clipper()->set_position(-1., false);
+ });
+ }
+ }
+
+ ImGui::SameLine(clipping_slider_left);
+ ImGui::PushItemWidth(window_width - clipping_slider_left);
+ float clp_dist = m_c->object_clipper()->get_position();
+ if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
+ m_c->object_clipper()->set_position(clp_dist, true);
+ if (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 GLGizmoSeam::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->m_supported_facets.set(*m_triangle_selectors[idx].get());
+ }
+
+ if (updated)
+ m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
+}
+
+
+
+void GLGizmoSeam::update_from_model_object()
+{
+ wxBusyCursor wait;
+
+ const ModelObject* mo = m_c->selection_info()->model_object();
+ m_triangle_selectors.clear();
+
+ int volume_id = -1;
+ for (const ModelVolume* mv : mo->volumes) {
+ if (! mv->is_model_part())
+ continue;
+
+ ++volume_id;
+
+ // This mesh does not account for the possible Z up SLA offset.
+ const TriangleMesh* mesh = &mv->mesh();
+
+ m_triangle_selectors.emplace_back(std::make_unique(*mesh));
+ m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data());
+ }
+}
+
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp
new file mode 100644
index 0000000000..469ec9180c
--- /dev/null
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp
@@ -0,0 +1,42 @@
+#ifndef slic3r_GLGizmoSeam_hpp_
+#define slic3r_GLGizmoSeam_hpp_
+
+#include "GLGizmoPainterBase.hpp"
+
+namespace Slic3r {
+
+namespace GUI {
+
+class GLGizmoSeam : public GLGizmoPainterBase
+{
+public:
+ GLGizmoSeam(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
+ : GLGizmoPainterBase(parent, icon_filename, sprite_id) {}
+
+protected:
+ void on_render_input_window(float x, float y, float bottom_limit) override;
+ std::string on_get_name() const override;
+
+private:
+ bool on_init() override;
+ void on_render() const override;
+ void on_render_for_picking() const override {}
+
+ void update_model_object() const override;
+ void update_from_model_object() override;
+
+ void on_opening() override {}
+ void on_shutdown() override {}
+
+ // 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 m_desc;
+};
+
+
+
+} // namespace GUI
+} // namespace Slic3r
+
+
+#endif // slic3r_GLGizmoSeam_hpp_
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index 089e2c6ffc..1087c64d5a 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -16,6 +16,7 @@
#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoCut.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"
+#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/PresetBundle.hpp"
@@ -104,6 +105,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5));
m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6));
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "sla_supports.svg", 7));
+ m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8));
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
@@ -388,6 +390,7 @@ void GLGizmosManager::set_painter_gizmo_data()
return;
dynamic_cast(m_gizmos[FdmSupports].get())->set_painter_gizmo_data(m_parent.get_selection());
+ dynamic_cast(m_gizmos[Seam].get())->set_painter_gizmo_data(m_parent.get_selection());
}
// Returns true if the gizmo used the event to do something, false otherwise.
@@ -402,6 +405,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == FdmSupports)
return dynamic_cast(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
+ else if (m_current == Seam)
+ return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else
return false;
}
@@ -465,7 +470,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt)
{
bool processed = false;
- if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) {
+ if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) {
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;
@@ -607,7 +612,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
if (evt.LeftDown())
{
- if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports)
+ if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ||m_current == Seam)
&& gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
// the gizmo got the event and took some action, there is no need to do anything more
processed = true;
@@ -634,23 +639,24 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
// event was taken care of by the SlaSupports gizmo
processed = true;
}
- else if (evt.RightDown() && (selected_object_idx != -1) && m_current == FdmSupports
+ else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == FdmSupports || m_current == Seam)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos))
{
- // event was taken care of by the FdmSupports gizmo
+ // event was taken care of by the FdmSupports / Seam gizmo
processed = true;
}
- else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow))
+ else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1)
+ && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam))
// don't allow dragging objects with the Sla gizmo on
processed = true;
- else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports )
+ else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam )
&& gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
{
// the gizmo got the event and took some action, no need to do anything more here
m_parent.set_as_dirty();
processed = true;
}
- else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && !m_parent.is_mouse_dragging())
+ else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && !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
@@ -662,7 +668,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_parent.is_mouse_dragging())
+ else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging())
{
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
processed = true;
@@ -752,7 +758,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
case 'r' :
case 'R' :
{
- if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane))
+ if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && gizmo_event(SLAGizmoEventType::ResetClippingPlane))
processed = true;
break;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
index b8b78eceb0..6b965525d5 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
@@ -67,6 +67,7 @@ public:
Hollow,
SlaSupports,
FdmSupports,
+ Seam,
Undefined
};