mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-23 17:02:39 +00:00
427 lines
19 KiB
C++
427 lines
19 KiB
C++
#include "GLGizmoFuzzySkin.hpp"
|
|
|
|
#include "libslic3r/Model.hpp"
|
|
#include "libslic3r/Print.hpp"
|
|
|
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
|
#include "slic3r/GUI/GUI.hpp"
|
|
#include "slic3r/GUI/GUI_App.hpp"
|
|
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
|
#include "slic3r/GUI/ImGuiWrapper.hpp"
|
|
#include "slic3r/GUI/MsgDialog.hpp"
|
|
#include "slic3r/GUI/Plater.hpp"
|
|
#include "slic3r/Utils/UndoRedo.hpp"
|
|
#include "GLGizmoUtils.hpp"
|
|
|
|
#include <glad/gl.h>
|
|
#include <algorithm>
|
|
|
|
namespace Slic3r::GUI {
|
|
|
|
void GLGizmoFuzzySkin::on_shutdown()
|
|
{
|
|
m_parent.use_slope(false);
|
|
m_parent.toggle_model_objects_visibility(true);
|
|
}
|
|
|
|
std::string GLGizmoFuzzySkin::on_get_name() const
|
|
{
|
|
return _u8L("Paint-on fuzzy skin");
|
|
}
|
|
|
|
bool GLGizmoFuzzySkin::on_init()
|
|
{
|
|
m_shortcut_key = WXK_CONTROL_H;
|
|
|
|
const wxString ctrl = GUI::shortkey_ctrl_prefix();
|
|
const wxString alt = GUI::shortkey_alt_prefix();
|
|
const wxString shift = GUI::shortkey_shift_prefix();
|
|
|
|
m_desc["reset_direction"] = _L("Reset direction");
|
|
m_desc["remove_all"] = _L("Erase all");
|
|
m_desc["circle"] = _L("Circle");
|
|
m_desc["sphere"] = _L("Sphere");
|
|
m_desc["pointer"] = _L("Triangles");
|
|
m_desc["tool_type"] = _L("Tool type");
|
|
m_desc["tool_brush"] = _L("Brush");
|
|
m_desc["tool_smart_fill"] = _L("Smart fill");
|
|
m_desc["clipping_of_view"] = _L("Section view");
|
|
m_desc["cursor_size"] = _L("Brush size");
|
|
m_desc["add_fuzzy_skin"] = _L("Add fuzzy skin");
|
|
m_desc["remove_fuzzy_skin"] = _L("Remove fuzzy skin");
|
|
m_desc["smart_fill_angle"] = _L("Smart fill angle");
|
|
|
|
std::pair<wxString, wxString> add_fuzzy_skin_shortcut = {_L("Left mouse button"), m_desc["add_fuzzy_skin"]};
|
|
std::pair<wxString, wxString> remove_fuzzy_skin_shortcut = {shift + _L("Left mouse button"), m_desc["remove_fuzzy_skin"]};
|
|
std::pair<wxString, wxString> clipping_shortcut = {alt + _L("Mouse wheel"), m_desc["clipping_of_view"]};
|
|
|
|
m_shortcuts_brush = {
|
|
add_fuzzy_skin_shortcut,
|
|
remove_fuzzy_skin_shortcut,
|
|
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
|
|
clipping_shortcut
|
|
};
|
|
|
|
m_shortcuts_triangle = {
|
|
add_fuzzy_skin_shortcut,
|
|
remove_fuzzy_skin_shortcut,
|
|
clipping_shortcut
|
|
};
|
|
|
|
m_shortcuts_smart_fill = {
|
|
add_fuzzy_skin_shortcut,
|
|
remove_fuzzy_skin_shortcut,
|
|
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
|
|
clipping_shortcut
|
|
};
|
|
|
|
return true;
|
|
}
|
|
|
|
GLGizmoFuzzySkin::GLGizmoFuzzySkin(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
|
|
: GLGizmoPainterBase(parent, icon_filename, sprite_id), m_current_tool(ImGui::CircleButtonIcon)
|
|
{
|
|
}
|
|
|
|
void GLGizmoFuzzySkin::render_painter_gizmo()
|
|
{
|
|
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();
|
|
m_c->instances_hider()->render_cut();
|
|
render_cursor();
|
|
|
|
glsafe(::glDisable(GL_BLEND));
|
|
}
|
|
|
|
void GLGizmoFuzzySkin::render_tooltip_button(float x, float y)
|
|
{
|
|
auto get_shortcuts = [this]() -> std::vector<std::pair<wxString, wxString>> {
|
|
switch (m_tool_type) {
|
|
case ToolType::BRUSH: return m_cursor_type == TriangleSelector::POINTER ? m_shortcuts_triangle : m_shortcuts_brush;
|
|
|
|
case ToolType::SMART_FILL: return m_shortcuts_smart_fill;
|
|
default: return {};
|
|
}
|
|
};
|
|
|
|
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, get_shortcuts(), x, y);
|
|
}
|
|
|
|
void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_limit)
|
|
{
|
|
ModelObject *mo = m_c->selection_info()->model_object();
|
|
if (!mo)
|
|
return;
|
|
|
|
float scale = m_parent.get_scale();
|
|
#ifdef WIN32
|
|
int dpi = get_dpi_for_window(wxGetApp().GetTopWindow());
|
|
scale *= (float) dpi / (float) DPI_DEFAULT;
|
|
#endif // WIN32
|
|
|
|
const DynamicPrintConfig &obj_cfg = mo->config.get();
|
|
|
|
const float approx_height = m_imgui->scaled(22.f);
|
|
y = std::min(y, bottom_limit - approx_height);
|
|
//BBS: GUI refactor: move gizmo to the right
|
|
#if BBS_TOOLBAR_ON_TOP
|
|
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
|
|
#else
|
|
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 1.0f, 0.0f);
|
|
#endif
|
|
//m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
|
|
|
// BBS
|
|
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
|
|
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
|
|
|
|
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
|
|
|
// 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 space_size = m_imgui->get_style_scaling() * 8;
|
|
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f),
|
|
m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2);
|
|
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
|
|
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f);
|
|
|
|
const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
|
|
const float cursor_type_radio_sphere = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f);
|
|
const float cursor_type_radio_pointer = m_imgui->calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f);
|
|
|
|
const float buttons_width = m_imgui->scaled(0.5f);
|
|
const float minimal_slider_width = m_imgui->scaled(4.f);
|
|
|
|
const float tool_type_radio_left = m_imgui->calc_text_size(m_desc["tool_type"]).x + m_imgui->scaled(1.f);
|
|
const float tool_type_radio_brush = m_imgui->calc_text_size(m_desc["tool_brush"]).x + m_imgui->scaled(2.5f);
|
|
const float tool_type_radio_smart_fill = m_imgui->calc_text_size(m_desc["tool_smart_fill"]).x + m_imgui->scaled(2.5f);
|
|
|
|
float caption_max = 0.f;
|
|
float total_text_max = 0.f;
|
|
for (const std::string t : {"add_fuzzy_skin", "remove_fuzzy_skin"}) {
|
|
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
|
|
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
|
|
}
|
|
total_text_max += caption_max + m_imgui->scaled(1.f);
|
|
caption_max += m_imgui->scaled(1.f);
|
|
|
|
const float circle_max_width = std::max(clipping_slider_left, cursor_slider_left);
|
|
|
|
const float sliders_left_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left));
|
|
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
|
|
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
|
|
const float empty_button_width = m_imgui->calc_button_size("").x;
|
|
|
|
window_width = std::max(window_width, total_text_max);
|
|
window_width = std::max(window_width, cursor_type_radio_circle + cursor_type_radio_sphere + cursor_type_radio_pointer);
|
|
window_width = std::max(window_width, tool_type_radio_left + tool_type_radio_brush + tool_type_radio_smart_fill);
|
|
window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f));
|
|
|
|
const float sliders_width = m_imgui->scaled(7.0f);
|
|
const float drag_left_width = ImGui::GetStyle().WindowPadding.x + sliders_width - space_size;
|
|
|
|
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
m_imgui->text(m_desc["tool_type"]);
|
|
|
|
std::array<wchar_t, 4> tool_ids;
|
|
tool_ids = { ImGui::CircleButtonIcon, ImGui::SphereButtonIcon, ImGui::TriangleButtonIcon, ImGui::FillButtonIcon };
|
|
std::array<wchar_t, 4> icons;
|
|
if (m_is_dark_mode)
|
|
icons = { ImGui::CircleButtonDarkIcon, ImGui::SphereButtonDarkIcon, ImGui::TriangleButtonDarkIcon, ImGui::FillButtonDarkIcon };
|
|
else
|
|
icons = { ImGui::CircleButtonIcon, ImGui::SphereButtonIcon, ImGui::TriangleButtonIcon, ImGui::FillButtonIcon };
|
|
std::array<wxString, 4> tool_tips = { _L("Circle"), _L("Sphere"), _L("Triangle"), _L("Fill") };
|
|
for (int i = 0; i < tool_ids.size(); i++) {
|
|
//std::string str_label = std::string("");
|
|
//std::wstring btn_name = icons[i] + boost::nowide::widen(str_label);
|
|
|
|
if (i != 0) ImGui::SameLine((empty_button_width + m_imgui->scaled(1.75f)) * i + m_imgui->scaled(1.5f));
|
|
|
|
bool is_active = m_current_tool == tool_ids[i];
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding , 3.f * scale);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding , ImVec2(4.f * scale, 4.f * scale));
|
|
ImGui::PushStyleColor(ImGuiCol_Text , ImVec4(1,1,1,1)); // ORCA Fixes icon rendered without colors while using Light theme
|
|
ImGui::PushStyleColor(ImGuiCol_Button , is_active ? ImVec4(0.f, .59f, .53f, .25f) : ImVec4(0,0,0,0)); // ORCA
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, is_active ? ImVec4(0.f, .59f, .53f, .25f) : ImVec4(.6f,.6f,.6f,.2f)); // ORCA
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive , is_active ? ImVec4(0.f, .59f, .53f, .30f) : ImVec4(0,0,0,0)); // ORCA
|
|
ImGui::PushStyleColor(ImGuiCol_Border , is_active ? ImGuiWrapper::COL_ORCA : ImVec4(0,0,0,0)); // ORCA
|
|
ImGui::PushStyleColor(ImGuiCol_BorderActive , is_active ? ImGuiWrapper::COL_ORCA : ImVec4(0,0,0,0)); // ORCA matched color for fixing flicker on click
|
|
bool btn_clicked = m_imgui->glyph_button(icons[i], ImVec2(16.f * scale, 16.f * scale)); // ORCA glyph_button for fixing unequal paddings
|
|
ImGui::PopStyleColor(6);
|
|
ImGui::PopStyleVar(3);
|
|
|
|
if (btn_clicked && m_current_tool != tool_ids[i]) {
|
|
m_current_tool = tool_ids[i];
|
|
for (auto &triangle_selector : m_triangle_selectors) {
|
|
triangle_selector->seed_fill_unselect_all_triangles();
|
|
triangle_selector->request_update_render_data();
|
|
}
|
|
}
|
|
|
|
if (ImGui::IsItemHovered()) {
|
|
m_imgui->tooltip(tool_tips[i], max_tooltip_width);
|
|
}
|
|
}
|
|
|
|
ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1));
|
|
|
|
if (m_current_tool == ImGui::CircleButtonIcon || m_current_tool == ImGui::SphereButtonIcon) {
|
|
if (m_current_tool == ImGui::CircleButtonIcon)
|
|
m_cursor_type = TriangleSelector::CursorType::CIRCLE;
|
|
else
|
|
m_cursor_type = TriangleSelector::CursorType::SPHERE;
|
|
m_tool_type = ToolType::BRUSH;
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
m_imgui->text(m_desc.at("cursor_size"));
|
|
ImGui::SameLine(circle_max_width);
|
|
ImGui::PushItemWidth(sliders_width);
|
|
m_imgui->bbl_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true);
|
|
ImGui::SameLine(drag_left_width + circle_max_width);
|
|
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
|
ImGui::BBLDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f");
|
|
} else if (m_current_tool == ImGui::TriangleButtonIcon) {
|
|
m_cursor_type = TriangleSelector::CursorType::POINTER;
|
|
m_tool_type = ToolType::BRUSH;
|
|
} else {
|
|
assert(m_current_tool == ImGui::FillButtonIcon);
|
|
m_cursor_type = TriangleSelector::CursorType::POINTER;
|
|
m_tool_type = ToolType::SMART_FILL;
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
m_imgui->text(m_desc["smart_fill_angle"]);
|
|
std::string format_str = std::string("%.f") + I18N::translate_utf8("°",
|
|
"Face angle threshold,"
|
|
"placed after the number with no whitespace in between.");
|
|
ImGui::SameLine(sliders_left_width);
|
|
ImGui::PushItemWidth(sliders_width);
|
|
if (m_imgui->bbl_slider_float_style("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax,
|
|
format_str.data(), 1.0f, true))
|
|
for (auto& triangle_selector : m_triangle_selectors) {
|
|
triangle_selector->seed_fill_unselect_all_triangles();
|
|
triangle_selector->request_update_render_data();
|
|
}
|
|
ImGui::SameLine(drag_left_width + sliders_left_width);
|
|
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
|
ImGui::BBLDragFloat("##smart_fill_angle_input", &m_smart_fill_angle, 0.05f, 0.0f, 0.0f, "%.2f");
|
|
}
|
|
|
|
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_by_ratio(-1., false);
|
|
});
|
|
}
|
|
}
|
|
|
|
auto clp_dist = float(m_c->object_clipper()->get_position());
|
|
ImGui::SameLine(sliders_left_width);
|
|
|
|
ImGui::PushItemWidth(sliders_width);
|
|
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
|
|
|
|
ImGui::SameLine(drag_left_width + sliders_left_width);
|
|
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
|
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
|
|
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
|
|
|
|
ImGui::Separator();
|
|
|
|
render_tooltip_button(x, y);
|
|
|
|
ImGui::SameLine();
|
|
m_imgui->disabled_begin(mo->is_fuzzy_skin_painted() == false);
|
|
if (m_imgui->button(m_desc.at("remove_all"))) {
|
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset selection"), UndoRedo::SnapshotType::GizmoAction);
|
|
int idx = -1;
|
|
for (ModelVolume *mv : mo->volumes)
|
|
if (mv->is_model_part()) {
|
|
++idx;
|
|
m_triangle_selectors[idx]->reset();
|
|
m_triangle_selectors[idx]->request_update_render_data(true);
|
|
}
|
|
|
|
update_model_object();
|
|
m_parent.set_as_dirty();
|
|
}
|
|
m_imgui->disabled_end();
|
|
|
|
ImGui::SameLine();
|
|
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
|
|
if (m_imgui->button(_L("Done"))) {
|
|
m_parent.reset_all_gizmos();
|
|
}
|
|
|
|
const DynamicPrintConfig &glb_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
|
const bool has_object_fuzzy_override = obj_cfg.option("fuzzy_skin");
|
|
const FuzzySkinType effective_fuzzy_skin_state = has_object_fuzzy_override ? obj_cfg.opt_enum<FuzzySkinType>("fuzzy_skin")
|
|
: glb_cfg.opt_enum<FuzzySkinType>("fuzzy_skin");
|
|
if (effective_fuzzy_skin_state == FuzzySkinType::Disabled_fuzzy) {
|
|
float font_size = ImGui::GetFontSize();
|
|
auto link_text = [&]() {
|
|
ImColor HyperColor = ImGuiWrapper::COL_ORCA;
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_WARNING);
|
|
float parent_width = ImGui::GetContentRegionAvail().x;
|
|
m_imgui->text_wrapped(_L("Warning: Fuzzy skin is disabled, painted fuzzy skin will not take effect!"), parent_width);
|
|
ImGui::PopStyleColor();
|
|
ImGui::PushStyleColor(ImGuiCol_Text, HyperColor.Value);
|
|
ImGui::Dummy(ImVec2(font_size * 1.8f, font_size * 1.3f));
|
|
ImGui::SameLine();
|
|
m_imgui->bold_text(_u8L("Enable painted fuzzy skin for this object"));
|
|
ImGui::PopStyleColor();
|
|
ImVec2 line_end = ImGui::GetItemRectMax();
|
|
line_end.y -= 2.0f;
|
|
ImVec2 line_start = line_end;
|
|
line_start.x = ImGui::GetItemRectMin().x;
|
|
ImGui::GetWindowDrawList()->AddLine(line_start, line_end, HyperColor);
|
|
if (ImGui::IsMouseHoveringRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), true)
|
|
&& ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
|
DynamicPrintConfig new_conf = obj_cfg;
|
|
new_conf.set_key_value("fuzzy_skin", new ConfigOptionEnum<FuzzySkinType>(FuzzySkinType::None));
|
|
mo->config.assign_config(new_conf);
|
|
}
|
|
};
|
|
ImGui::Separator();
|
|
|
|
link_text();
|
|
}
|
|
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
|
|
GizmoImguiEnd();
|
|
|
|
// BBS
|
|
ImGuiWrapper::pop_toolbar_style();
|
|
}
|
|
|
|
void GLGizmoFuzzySkin::update_model_object()
|
|
{
|
|
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->fuzzy_skin_facets.set(*m_triangle_selectors[idx]);
|
|
}
|
|
|
|
if (updated) {
|
|
const ModelObjectPtrs &mos = wxGetApp().model().objects;
|
|
wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin());
|
|
|
|
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
|
}
|
|
}
|
|
|
|
void GLGizmoFuzzySkin::update_from_model_object(bool first_update)
|
|
{
|
|
wxBusyCursor wait;
|
|
|
|
const ModelObject *mo = m_c->selection_info()->model_object();
|
|
m_triangle_selectors.clear();
|
|
|
|
int volume_id = -1;
|
|
std::vector<ColorRGBA> ebt_colors;
|
|
ebt_colors.push_back(GLVolume::NEUTRAL_COLOR);
|
|
ebt_colors.push_back(TriangleSelectorGUI::enforcers_color);
|
|
ebt_colors.push_back(TriangleSelectorGUI::blockers_color);
|
|
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<TriangleSelectorPatch>(*mesh, ebt_colors));
|
|
// Reset of TriangleSelector is done inside TriangleSelectorGUI's constructor, so we don't need it to perform it again in deserialize().
|
|
m_triangle_selectors.back()->deserialize(mv->fuzzy_skin_facets.get_data(), false);
|
|
m_triangle_selectors.back()->request_update_render_data();
|
|
}
|
|
}
|
|
|
|
PainterGizmoType GLGizmoFuzzySkin::get_painter_type() const
|
|
{
|
|
return PainterGizmoType::FUZZY_SKIN;
|
|
}
|
|
|
|
wxString GLGizmoFuzzySkin::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const
|
|
{
|
|
return shift_down ? _L("Remove fuzzy skin") : _L("Add fuzzy skin");
|
|
}
|
|
|
|
} // namespace Slic3r::GUI
|