Merge branch 'lh_fdm_supports_smart_fill'

This commit is contained in:
Lukáš Hejl 2021-10-07 13:14:40 +02:00
commit d2cd14ba7d
7 changed files with 407 additions and 254 deletions

View File

@ -51,10 +51,17 @@ bool GLGizmoFdmSupports::on_init()
m_desc["remove_all"] = _L("Remove all selection"); m_desc["remove_all"] = _L("Remove all selection");
m_desc["circle"] = _L("Circle"); m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere"); m_desc["sphere"] = _L("Sphere");
m_desc["pointer"] = _L("Triangles");
m_desc["highlight_by_angle"] = _L("Highlight by angle"); m_desc["highlight_by_angle"] = _L("Highlight by angle");
m_desc["enforce_button"] = _L("Enforce"); m_desc["enforce_button"] = _L("Enforce");
m_desc["cancel"] = _L("Cancel"); m_desc["cancel"] = _L("Cancel");
m_desc["tool_type"] = _L("Tool type") + ": ";
m_desc["tool_brush"] = _L("Brush");
m_desc["tool_smart_fill"] = _L("Smart fill");
m_desc["smart_fill_angle"] = _L("Smart fill angle");
return true; return true;
} }
@ -82,42 +89,48 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
if (! m_c->selection_info()->model_object()) if (! m_c->selection_info()->model_object())
return; return;
const float approx_height = m_imgui->scaled(17.0f); const float approx_height = m_imgui->scaled(20.5f);
y = std::min(y, bottom_limit - approx_height); y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); m_imgui->begin(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: // 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, 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->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
+ 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 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("highlight_by_angle")).x + m_imgui->scaled(1.f);
const float autoset_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle")).x + m_imgui->scaled(1.f); const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_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 const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
+ 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_width2 = m_imgui->calc_text_size(m_desc["sphere"]).x const float cursor_type_radio_pointer = m_imgui->calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f);
+ 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 button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
const float button_enforce_width = m_imgui->calc_text_size(m_desc.at("enforce_button")).x; const float button_enforce_width = m_imgui->calc_text_size(m_desc.at("enforce_button")).x;
const float button_cancel_width = m_imgui->calc_text_size(m_desc.at("cancel")).x; const float button_cancel_width = m_imgui->calc_text_size(m_desc.at("cancel")).x;
const float buttons_width = std::max(button_enforce_width, button_cancel_width) + m_imgui->scaled(0.5f); const float buttons_width = std::max(button_enforce_width, button_cancel_width) + m_imgui->scaled(0.5f);
const float minimal_slider_width = m_imgui->scaled(4.f); 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 caption_max = 0.f;
float total_text_max = 0.f; float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) { for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t + "_caption")).x); caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
} }
caption_max += m_imgui->scaled(1.f); total_text_max += caption_max + m_imgui->scaled(1.f);
total_text_max += m_imgui->scaled(1.f); caption_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)); float sliders_width = std::max(std::max(autoset_slider_left, smart_fill_slider_left), std::max(cursor_slider_left, clipping_slider_left));
float window_width = minimal_slider_width + sliders_width;
window_width = std::max(window_width, total_text_max); window_width = std::max(window_width, total_text_max);
window_width = std::max(window_width, button_width); 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, 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)); 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) { auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
@ -129,7 +142,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"})
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
m_imgui->text("");
ImGui::Separator(); ImGui::Separator();
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
@ -138,9 +150,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
std::string format_str = std::string("%.f") + I18N::translate_utf8("°", std::string format_str = std::string("%.f") + I18N::translate_utf8("°",
"Degree sign to use in the respective slider in FDM supports gizmo," "Degree sign to use in the respective slider in FDM supports gizmo,"
"placed after the number with no whitespace in between."); "placed after the number with no whitespace in between.");
ImGui::SameLine(autoset_slider_left); ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - autoset_slider_left); ImGui::PushItemWidth(window_width - sliders_width);
if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, format_str.data())) { if (m_imgui->slider_float("##angle_threshold_deg", &m_angle_threshold_deg, 0.f, 90.f, format_str.data())) {
m_parent.set_slope_normal_angle(90.f - m_angle_threshold_deg); m_parent.set_slope_normal_angle(90.f - m_angle_threshold_deg);
if (! m_parent.is_using_slope()) { if (! m_parent.is_using_slope()) {
m_parent.use_slope(true); m_parent.use_slope(true);
@ -163,79 +175,136 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
} }
m_imgui->disabled_end(); m_imgui->disabled_end();
ImGui::Separator();
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"),
UndoRedo::SnapshotType::GizmoAction);
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();
m_triangle_selectors[idx]->request_update_render_data();
}
}
update_model_object();
m_parent.set_as_dirty();
}
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
ImGui::AlignTextToFramePadding(); ImGui::Separator();
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(cursor_slider_left);
ImGui::PushItemWidth(window_width - cursor_slider_left);
m_imgui->slider_float(" ", &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(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_type")); m_imgui->text(m_desc["tool_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; float tool_type_offset = tool_type_radio_left + (window_width - tool_type_radio_left - tool_type_radio_brush - tool_type_radio_smart_fill + m_imgui->scaled(0.5f)) / 2.f;
if (m_imgui->radio_button(m_desc["sphere"], sphere_sel)) ImGui::SameLine(tool_type_offset);
sphere_sel = true; ImGui::PushItemWidth(tool_type_radio_brush);
if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == ToolType::BRUSH))
m_tool_type = ToolType::BRUSH;
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width); ImGui::PushTextWrapPos(max_tooltip_width);
ImGui::TextUnformatted(_L("Paints all facets inside, regardless of their orientation.").ToUTF8().data()); ImGui::TextUnformatted(_L("Paints facets according to the chosen painting brush.").ToUTF8().data());
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::SameLine(cursor_type_radio_left + cursor_type_radio_width2 + m_imgui->scaled(0.f)); ImGui::SameLine(tool_type_offset + tool_type_radio_brush);
ImGui::PushItemWidth(cursor_type_radio_width2); ImGui::PushItemWidth(tool_type_radio_smart_fill);
if (m_imgui->radio_button(m_desc["tool_smart_fill"], m_tool_type == ToolType::SMART_FILL))
if (m_imgui->radio_button(m_desc["circle"], ! sphere_sel)) m_tool_type = ToolType::SMART_FILL;
sphere_sel = false;
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width); ImGui::PushTextWrapPos(max_tooltip_width);
ImGui::TextUnformatted(_L("Ignores facets facing away from the camera.").ToUTF8().data()); ImGui::TextUnformatted(_L("Paints neighboring facets whose relative angle is less or equal to set angle.").ToUTF8().data());
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
m_cursor_type = sphere_sel ImGui::Separator();
? TriangleSelector::CursorType::SPHERE
: TriangleSelector::CursorType::CIRCLE;
if (m_tool_type == ToolType::BRUSH) {
m_imgui->text(m_desc.at("cursor_type"));
ImGui::NewLine();
float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(1.5f)) / 2.f;
ImGui::SameLine(cursor_type_offset);
ImGui::PushItemWidth(cursor_type_radio_sphere);
if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE))
m_cursor_type = TriangleSelector::CursorType::SPHERE;
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_offset + cursor_type_radio_sphere);
ImGui::PushItemWidth(cursor_type_radio_circle);
if (m_imgui->radio_button(m_desc["circle"], m_cursor_type == TriangleSelector::CursorType::CIRCLE))
m_cursor_type = TriangleSelector::CursorType::CIRCLE;
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();
}
ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere + cursor_type_radio_circle);
ImGui::PushItemWidth(cursor_type_radio_pointer);
if (m_imgui->radio_button(m_desc["pointer"], m_cursor_type == TriangleSelector::CursorType::POINTER))
m_cursor_type = TriangleSelector::CursorType::POINTER;
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
ImGui::TextUnformatted(_L("Paints only one facet.").ToUTF8().data());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
m_imgui->disabled_begin(m_cursor_type != TriangleSelector::CursorType::SPHERE && m_cursor_type != TriangleSelector::CursorType::CIRCLE);
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - sliders_width);
m_imgui->slider_float("##cursor_radius", &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();
}
m_imgui->checkbox(_L("Split triangles"), m_triangle_splitting_enabled);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
ImGui::TextUnformatted(_L("Split bigger facets into smaller ones while the object is painted.").ToUTF8().data());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
m_imgui->disabled_end();
} else {
assert(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("°", "Degree sign to use in the respective slider in MMU gizmo,"
"placed after the number with no whitespace in between.");
ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - sliders_width);
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data()))
for (auto &triangle_selector : m_triangle_selectors) {
triangle_selector->seed_fill_unselect_all_triangles();
triangle_selector->request_update_render_data();
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
ImGui::Separator(); ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f) { if (m_c->object_clipper()->get_position() == 0.f) {
@ -250,10 +319,10 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
} }
} }
ImGui::SameLine(clipping_slider_left); ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - clipping_slider_left); ImGui::PushItemWidth(window_width - sliders_width);
auto clp_dist = float(m_c->object_clipper()->get_position()); auto clp_dist = float(m_c->object_clipper()->get_position());
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true); m_c->object_clipper()->set_position(clp_dist, true);
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
@ -263,6 +332,23 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::Separator();
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"), UndoRedo::SnapshotType::GizmoAction);
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();
m_triangle_selectors[idx]->request_update_render_data();
}
update_model_object();
m_parent.set_as_dirty();
}
m_imgui->end(); m_imgui->end();
} }
@ -291,7 +377,7 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
// Now calculate dot product of vert_direction and facets' normals. // Now calculate dot product of vert_direction and facets' normals.
int idx = 0; int idx = 0;
const indexed_triangle_set &its = mv->mesh().its; const indexed_triangle_set &its = mv->mesh().its;
for (stl_triangle_vertex_indices face : its.indices) { for (const stl_triangle_vertex_indices &face : its.indices) {
if (its_face_normal(its, face).dot(down) > dot_limit) { if (its_face_normal(its, face).dot(down) > dot_limit) {
m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER); m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER);
m_triangle_selectors.back()->request_update_render_data(); m_triangle_selectors.back()->request_update_render_data();

View File

@ -42,7 +42,6 @@ void GLGizmoMmuSegmentation::on_shutdown()
std::string GLGizmoMmuSegmentation::on_get_name() const std::string GLGizmoMmuSegmentation::on_get_name() const
{ {
// FIXME Lukas H.: Discuss and change shortcut
return _u8L("Multimaterial painting"); return _u8L("Multimaterial painting");
} }
@ -107,7 +106,6 @@ void GLGizmoMmuSegmentation::init_extruders_data()
bool GLGizmoMmuSegmentation::on_init() bool GLGizmoMmuSegmentation::on_init()
{ {
// FIXME Lukas H.: Discuss and change shortcut
m_shortcut_key = WXK_CONTROL_N; m_shortcut_key = WXK_CONTROL_N;
m_desc["reset_direction"] = _L("Reset direction"); m_desc["reset_direction"] = _L("Reset direction");
@ -123,7 +121,7 @@ bool GLGizmoMmuSegmentation::on_init()
m_desc["remove_all"] = _L("Remove all painted areas"); m_desc["remove_all"] = _L("Remove all painted areas");
m_desc["circle"] = _L("Circle"); m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere"); m_desc["sphere"] = _L("Sphere");
m_desc["pointer"] = _L("Pointer"); m_desc["pointer"] = _L("Triangles");
m_desc["tool_type"] = _L("Tool type"); m_desc["tool_type"] = _L("Tool type");
m_desc["tool_brush"] = _L("Brush"); m_desc["tool_brush"] = _L("Brush");
@ -236,7 +234,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
if (!m_c->selection_info()->model_object()) if (!m_c->selection_info()->model_object())
return; return;
const float approx_height = m_imgui->scaled(25.0f); const float approx_height = m_imgui->scaled(22.0f);
y = std::min(y, bottom_limit - approx_height); y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
@ -264,13 +262,13 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
const float tool_type_radio_smart_fill = m_imgui->calc_text_size(m_desc["tool_smart_fill"]).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 caption_max = 0.f;
float total_text_max = 0.; float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"}) { for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t + "_caption")).x); caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
} }
caption_max += m_imgui->scaled(1.f); total_text_max += caption_max + m_imgui->scaled(1.f);
total_text_max += m_imgui->scaled(1.f); caption_max += m_imgui->scaled(1.f);
float sliders_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left)); float sliders_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left));
float window_width = minimal_slider_width + sliders_width; float window_width = minimal_slider_width + sliders_width;
@ -289,7 +287,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"}) for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"})
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
m_imgui->text("");
ImGui::Separator(); ImGui::Separator();
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
@ -321,15 +318,13 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::Separator(); ImGui::Separator();
m_imgui->text(m_desc.at("tool_type")); m_imgui->text(m_desc.at("tool_type"));
float tool_type_offset = (window_width - tool_type_radio_brush - tool_type_radio_bucket_fill - tool_type_radio_smart_fill + m_imgui->scaled(2.f)) / 2.f;
ImGui::NewLine(); ImGui::NewLine();
ImGui::SameLine(tool_type_offset + m_imgui->scaled(0.f)); float tool_type_offset = (window_width - tool_type_radio_brush - tool_type_radio_bucket_fill - tool_type_radio_smart_fill + m_imgui->scaled(1.5f)) / 2.f;
ImGui::SameLine(tool_type_offset);
ImGui::PushItemWidth(tool_type_radio_brush); ImGui::PushItemWidth(tool_type_radio_brush);
if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BRUSH)) { if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == ToolType::BRUSH)) {
m_tool_type = GLGizmoMmuSegmentation::ToolType::BRUSH; m_tool_type = ToolType::BRUSH;
for (auto &triangle_selector : m_triangle_selectors) { for (auto &triangle_selector : m_triangle_selectors) {
triangle_selector->seed_fill_unselect_all_triangles(); triangle_selector->seed_fill_unselect_all_triangles();
triangle_selector->request_update_render_data(); triangle_selector->request_update_render_data();
@ -344,10 +339,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::SameLine(tool_type_offset + tool_type_radio_brush + m_imgui->scaled(0.f)); ImGui::SameLine(tool_type_offset + tool_type_radio_brush);
ImGui::PushItemWidth(tool_type_radio_smart_fill); ImGui::PushItemWidth(tool_type_radio_smart_fill);
if (m_imgui->radio_button(m_desc["tool_smart_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::SMART_FILL)) { if (m_imgui->radio_button(m_desc["tool_smart_fill"], m_tool_type == ToolType::SMART_FILL)) {
m_tool_type = GLGizmoMmuSegmentation::ToolType::SMART_FILL; m_tool_type = ToolType::SMART_FILL;
for (auto &triangle_selector : m_triangle_selectors) { for (auto &triangle_selector : m_triangle_selectors) {
triangle_selector->seed_fill_unselect_all_triangles(); triangle_selector->seed_fill_unselect_all_triangles();
triangle_selector->request_update_render_data(); triangle_selector->request_update_render_data();
@ -362,10 +357,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::SameLine(tool_type_offset + tool_type_radio_brush + tool_type_radio_smart_fill + m_imgui->scaled(0.f)); ImGui::SameLine(tool_type_offset + tool_type_radio_brush + tool_type_radio_smart_fill);
ImGui::PushItemWidth(tool_type_radio_bucket_fill); ImGui::PushItemWidth(tool_type_radio_bucket_fill);
if (m_imgui->radio_button(m_desc["tool_bucket_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BUCKET_FILL)) { if (m_imgui->radio_button(m_desc["tool_bucket_fill"], m_tool_type == ToolType::BUCKET_FILL)) {
m_tool_type = GLGizmoMmuSegmentation::ToolType::BUCKET_FILL; m_tool_type = ToolType::BUCKET_FILL;
for (auto &triangle_selector : m_triangle_selectors) { for (auto &triangle_selector : m_triangle_selectors) {
triangle_selector->seed_fill_unselect_all_triangles(); triangle_selector->seed_fill_unselect_all_triangles();
triangle_selector->request_update_render_data(); triangle_selector->request_update_render_data();
@ -386,8 +381,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
m_imgui->text(m_desc.at("cursor_type")); m_imgui->text(m_desc.at("cursor_type"));
ImGui::NewLine(); ImGui::NewLine();
float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(2.f)) / 2.f; float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(1.5f)) / 2.f;
ImGui::SameLine(cursor_type_offset + m_imgui->scaled(0.f)); ImGui::SameLine(cursor_type_offset);
ImGui::PushItemWidth(cursor_type_radio_sphere); ImGui::PushItemWidth(cursor_type_radio_sphere);
if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE)) if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE))
m_cursor_type = TriangleSelector::CursorType::SPHERE; m_cursor_type = TriangleSelector::CursorType::SPHERE;
@ -400,7 +395,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::SameLine(cursor_type_offset +cursor_type_radio_sphere + m_imgui->scaled(0.f)); ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere);
ImGui::PushItemWidth(cursor_type_radio_circle); ImGui::PushItemWidth(cursor_type_radio_circle);
if (m_imgui->radio_button(m_desc["circle"], m_cursor_type == TriangleSelector::CursorType::CIRCLE)) if (m_imgui->radio_button(m_desc["circle"], m_cursor_type == TriangleSelector::CursorType::CIRCLE))
@ -414,7 +409,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere + cursor_type_radio_circle + m_imgui->scaled(0.f)); ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere + cursor_type_radio_circle);
ImGui::PushItemWidth(cursor_type_radio_pointer); ImGui::PushItemWidth(cursor_type_radio_pointer);
if (m_imgui->radio_button(m_desc["pointer"], m_cursor_type == TriangleSelector::CursorType::POINTER)) if (m_imgui->radio_button(m_desc["pointer"], m_cursor_type == TriangleSelector::CursorType::POINTER))
@ -434,7 +429,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
m_imgui->text(m_desc.at("cursor_size")); m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(sliders_width); ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - sliders_width); ImGui::PushItemWidth(window_width - sliders_width);
m_imgui->slider_float(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width); ImGui::PushTextWrapPos(max_tooltip_width);
@ -492,8 +487,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::SameLine(sliders_width); ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - sliders_width); ImGui::PushItemWidth(window_width - sliders_width);
auto clp_dist = float(m_c->object_clipper()->get_position()); auto clp_dist = float(m_c->object_clipper()->get_position());
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true); m_c->object_clipper()->set_position(clp_dist, true);
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width); ImGui::PushTextWrapPos(max_tooltip_width);
@ -596,11 +592,6 @@ std::array<float, 4> GLGizmoMmuSegmentation::get_cursor_sphere_right_button_colo
return {color[0], color[1], color[2], 0.25f}; return {color[0], color[1], color[2], 0.25f};
} }
static std::array<float, 4> get_seed_fill_color(const std::array<float, 4> &base_color)
{
return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f};
}
void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
{ {
if (m_update_render_data) if (m_update_render_data)
@ -616,14 +607,14 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx) for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx)
if (m_gizmo_scene.has_VBOs(color_idx)) { if (m_gizmo_scene.has_VBOs(color_idx)) {
if (color_idx > m_colors.size()) // Seed fill VBO if (color_idx > m_colors.size()) // Seed fill VBO
shader->set_uniform("uniform_color", get_seed_fill_color(color_idx == (m_colors.size() + 1) ? m_default_volume_color : m_colors[color_idx - (m_colors.size() + 1) - 1])); shader->set_uniform("uniform_color", TriangleSelectorGUI::get_seed_fill_color(color_idx == (m_colors.size() + 1) ? m_default_volume_color : m_colors[color_idx - (m_colors.size() + 1) - 1]));
else // Normal VBO else // Normal VBO
shader->set_uniform("uniform_color", color_idx == 0 ? m_default_volume_color : m_colors[color_idx - 1]); shader->set_uniform("uniform_color", color_idx == 0 ? m_default_volume_color : m_colors[color_idx - 1]);
m_gizmo_scene.render(color_idx); m_gizmo_scene.render(color_idx);
} }
if (m_gizmo_scene.has_contour_VBO()) { if (m_paint_contour.has_VBO()) {
ScopeGuard guard_gouraud([shader]() { shader->start_using(); }); ScopeGuard guard_gouraud([shader]() { shader->start_using(); });
shader->stop_using(); shader->stop_using();
@ -631,7 +622,7 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
contour_shader->start_using(); contour_shader->start_using();
glsafe(::glDepthFunc(GL_LEQUAL)); glsafe(::glDepthFunc(GL_LEQUAL));
m_gizmo_scene.render_contour(); m_paint_contour.render();
glsafe(::glDepthFunc(GL_LESS)); glsafe(::glDepthFunc(GL_LESS));
contour_shader->stop_using(); contour_shader->stop_using();
@ -670,23 +661,24 @@ void TriangleSelectorMmGui::update_render_data()
m_gizmo_scene.finalize_triangle_indices(); m_gizmo_scene.finalize_triangle_indices();
m_paint_contour.release_geometry();
std::vector<Vec2i> contour_edges = this->get_seed_fill_contour(); std::vector<Vec2i> contour_edges = this->get_seed_fill_contour();
m_gizmo_scene.contour_vertices.reserve(contour_edges.size() * 6); m_paint_contour.contour_vertices.reserve(contour_edges.size() * 6);
for (const Vec2i &edge : contour_edges) { for (const Vec2i &edge : contour_edges) {
m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.x()); m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.x());
m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.y()); m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.y());
m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.z()); m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.z());
m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.x()); m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.x());
m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.y()); m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.y());
m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.z()); m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.z());
} }
m_gizmo_scene.contour_indices.assign(m_gizmo_scene.contour_vertices.size() / 3, 0); m_paint_contour.contour_indices.assign(m_paint_contour.contour_vertices.size() / 3, 0);
std::iota(m_gizmo_scene.contour_indices.begin(), m_gizmo_scene.contour_indices.end(), 0); std::iota(m_paint_contour.contour_indices.begin(), m_paint_contour.contour_indices.end(), 0);
m_gizmo_scene.contour_indices_size = m_gizmo_scene.contour_indices.size(); m_paint_contour.contour_indices_size = m_paint_contour.contour_indices.size();
m_gizmo_scene.finalize_contour(); m_paint_contour.finalize_geometry();
} }
wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const
@ -710,14 +702,6 @@ void GLMmSegmentationGizmo3DScene::release_geometry() {
glsafe(::glDeleteBuffers(1, &triangle_indices_VBO_id)); glsafe(::glDeleteBuffers(1, &triangle_indices_VBO_id));
triangle_indices_VBO_id = 0; triangle_indices_VBO_id = 0;
} }
if (this->contour_vertices_VBO_id) {
glsafe(::glDeleteBuffers(1, &this->contour_vertices_VBO_id));
this->contour_vertices_VBO_id = 0;
}
if (this->contour_indices_VBO_id) {
glsafe(::glDeleteBuffers(1, &this->contour_indices_VBO_id));
this->contour_indices_VBO_id = 0;
}
this->clear(); this->clear();
} }
@ -745,29 +729,6 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
} }
void GLMmSegmentationGizmo3DScene::render_contour() const
{
assert(this->contour_vertices_VBO_id != 0);
assert(this->contour_indices_VBO_id != 0);
glsafe(::glLineWidth(4.0f));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_vertices_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
if (this->contour_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->contour_indices_VBO_id));
glsafe(::glDrawElements(GL_LINES, GLsizei(this->contour_indices_size), GL_UNSIGNED_INT, nullptr));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
void GLMmSegmentationGizmo3DScene::finalize_vertices() void GLMmSegmentationGizmo3DScene::finalize_vertices()
{ {
assert(this->vertices_VBO_id == 0); assert(this->vertices_VBO_id == 0);
@ -795,26 +756,4 @@ void GLMmSegmentationGizmo3DScene::finalize_triangle_indices()
} }
} }
void GLMmSegmentationGizmo3DScene::finalize_contour()
{
assert(this->contour_vertices_VBO_id == 0);
assert(this->contour_indices_VBO_id == 0);
if (!this->contour_vertices.empty()) {
glsafe(::glGenBuffers(1, &this->contour_vertices_VBO_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_vertices_VBO_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_vertices.size() * sizeof(float), this->contour_vertices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
this->contour_vertices.clear();
}
if (!this->contour_indices.empty()) {
glsafe(::glGenBuffers(1, &this->contour_indices_VBO_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_indices_VBO_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
this->contour_indices.clear();
}
}
} // namespace Slic3r } // namespace Slic3r

View File

@ -25,8 +25,6 @@ public:
return this->triangle_indices_VBO_ids[triangle_indices_idx] != 0; return this->triangle_indices_VBO_ids[triangle_indices_idx] != 0;
} }
[[nodiscard]] inline bool has_contour_VBO() const { return this->contour_indices_VBO_id != 0; }
// Release the geometry data, release OpenGL VBOs. // Release the geometry data, release OpenGL VBOs.
void release_geometry(); void release_geometry();
// Finalize the initialization of the geometry, upload the geometry to OpenGL VBO objects // Finalize the initialization of the geometry, upload the geometry to OpenGL VBO objects
@ -35,9 +33,6 @@ public:
// Finalize the initialization of the indices, upload the indices to OpenGL VBO objects // Finalize the initialization of the indices, upload the indices to OpenGL VBO objects
// and possibly releasing it if it has been loaded into the VBOs. // and possibly releasing it if it has been loaded into the VBOs.
void finalize_triangle_indices(); void finalize_triangle_indices();
// Finalize the initialization of the contour geometry and the indices, upload both to OpenGL VBO objects
// and possibly releasing it if it has been loaded into the VBOs.
void finalize_contour();
void clear() void clear()
{ {
@ -47,34 +42,21 @@ public:
for (size_t &triangle_indices_size : this->triangle_indices_sizes) for (size_t &triangle_indices_size : this->triangle_indices_sizes)
triangle_indices_size = 0; triangle_indices_size = 0;
this->contour_vertices.clear();
this->contour_indices.clear();
this->contour_indices_size = 0;
} }
void render(size_t triangle_indices_idx) const; void render(size_t triangle_indices_idx) const;
void render_contour() const;
std::vector<float> vertices; std::vector<float> vertices;
std::vector<std::vector<int>> triangle_indices; std::vector<std::vector<int>> triangle_indices;
std::vector<float> contour_vertices;
std::vector<int> contour_indices;
// When the triangle indices are loaded into the graphics card as Vertex Buffer Objects, // When the triangle indices are loaded into the graphics card as Vertex Buffer Objects,
// the above mentioned std::vectors are cleared and the following variables keep their original length. // the above mentioned std::vectors are cleared and the following variables keep their original length.
std::vector<size_t> triangle_indices_sizes; std::vector<size_t> triangle_indices_sizes;
size_t contour_indices_size{0};
// IDs of the Vertex Array Objects, into which the geometry has been loaded. // IDs of the Vertex Array Objects, into which the geometry has been loaded.
// Zero if the VBOs are not sent to GPU yet. // Zero if the VBOs are not sent to GPU yet.
unsigned int vertices_VBO_id{0}; unsigned int vertices_VBO_id{0};
std::vector<unsigned int> triangle_indices_VBO_ids; std::vector<unsigned int> triangle_indices_VBO_ids;
unsigned int contour_vertices_VBO_id{0};
unsigned int contour_indices_VBO_id{0};
}; };
class TriangleSelectorMmGui : public TriangleSelectorGUI { class TriangleSelectorMmGui : public TriangleSelectorGUI {

View File

@ -541,7 +541,10 @@ void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive&)
m_schedule_update = true; m_schedule_update = true;
} }
std::array<float, 4> TriangleSelectorGUI::get_seed_fill_color(const std::array<float, 4> &base_color)
{
return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f};
}
void TriangleSelectorGUI::render(ImGuiWrapper* imgui) void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
{ {
@ -566,6 +569,29 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
} }
} }
for (auto &iva : m_iva_seed_fills)
if (iva.has_VBOs()) {
size_t color_idx = &iva - &m_iva_seed_fills.front();
const std::array<float, 4> &color = TriangleSelectorGUI::get_seed_fill_color(color_idx == 1 ? enforcers_color :
color_idx == 2 ? blockers_color :
GLVolume::NEUTRAL_COLOR);
shader->set_uniform("uniform_color", color);
iva.render();
}
if (m_paint_contour.has_VBO()) {
ScopeGuard guard_gouraud([shader]() { shader->start_using(); });
shader->stop_using();
auto *contour_shader = wxGetApp().get_shader("mm_contour");
contour_shader->start_using();
glsafe(::glDepthFunc(GL_GEQUAL));
m_paint_contour.render();
glsafe(::glDepthFunc(GL_LESS));
contour_shader->stop_using();
}
#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
if (imgui) if (imgui)
@ -575,26 +601,33 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
#endif #endif
} }
void TriangleSelectorGUI::update_render_data() void TriangleSelectorGUI::update_render_data()
{ {
int enf_cnt = 0; int enf_cnt = 0;
int blc_cnt = 0; int blc_cnt = 0;
std::vector<int> seed_fill_cnt(m_iva_seed_fills.size(), 0);
for (auto *iva : {&m_iva_enforcers, &m_iva_blockers}) for (auto *iva : {&m_iva_enforcers, &m_iva_blockers})
iva->release_geometry(); iva->release_geometry();
for (auto &iva : m_iva_seed_fills)
iva.release_geometry();
for (const Triangle &tr : m_triangles) { 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; continue;
GLIndexedVertexArray &iva = tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : m_iva_blockers; int tr_state = int(tr.get_state());
int & cnt = tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt; GLIndexedVertexArray &iva = tr.is_selected_by_seed_fill() ? m_iva_seed_fills[tr_state] :
tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers :
m_iva_blockers;
int &cnt = tr.is_selected_by_seed_fill() ? seed_fill_cnt[tr_state] :
tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt :
blc_cnt;
const Vec3f &v0 = m_vertices[tr.verts_idxs[0]].v; const Vec3f &v0 = m_vertices[tr.verts_idxs[0]].v;
const Vec3f &v1 = m_vertices[tr.verts_idxs[1]].v; const Vec3f &v1 = m_vertices[tr.verts_idxs[1]].v;
const Vec3f &v2 = m_vertices[tr.verts_idxs[2]].v; const Vec3f &v2 = m_vertices[tr.verts_idxs[2]].v;
//FIXME the normal may likely be pulled from m_triangle_selectors, but it may not be worth the effort //FIXME the normal may likely be pulled from m_triangle_selectors, but it may not be worth the effort
// or the current implementation may be more cache friendly. // or the current implementation may be more cache friendly.
const Vec3f n = (v1 - v0).cross(v2 - v1).normalized(); const Vec3f n = (v1 - v0).cross(v2 - v1).normalized();
iva.push_geometry(v0, n); iva.push_geometry(v0, n);
@ -606,9 +639,87 @@ void TriangleSelectorGUI::update_render_data()
for (auto *iva : {&m_iva_enforcers, &m_iva_blockers}) for (auto *iva : {&m_iva_enforcers, &m_iva_blockers})
iva->finalize_geometry(true); iva->finalize_geometry(true);
for (auto &iva : m_iva_seed_fills)
iva.finalize_geometry(true);
m_paint_contour.release_geometry();
std::vector<Vec2i> contour_edges = this->get_seed_fill_contour();
m_paint_contour.contour_vertices.reserve(contour_edges.size() * 6);
for (const Vec2i &edge : contour_edges) {
m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.x());
m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.y());
m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.z());
m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.x());
m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.y());
m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.z());
}
m_paint_contour.contour_indices.assign(m_paint_contour.contour_vertices.size() / 3, 0);
std::iota(m_paint_contour.contour_indices.begin(), m_paint_contour.contour_indices.end(), 0);
m_paint_contour.contour_indices_size = m_paint_contour.contour_indices.size();
m_paint_contour.finalize_geometry();
} }
void GLPaintContour::render() const
{
assert(this->m_contour_VBO_id != 0);
assert(this->m_contour_EBO_id != 0);
glsafe(::glLineWidth(4.0f));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_contour_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
if (this->contour_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_contour_EBO_id));
glsafe(::glDrawElements(GL_LINES, GLsizei(this->contour_indices_size), GL_UNSIGNED_INT, nullptr));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
void GLPaintContour::finalize_geometry()
{
assert(this->m_contour_VBO_id == 0);
assert(this->m_contour_EBO_id == 0);
if (!this->contour_vertices.empty()) {
glsafe(::glGenBuffers(1, &this->m_contour_VBO_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_contour_VBO_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_vertices.size() * sizeof(float), this->contour_vertices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
this->contour_vertices.clear();
}
if (!this->contour_indices.empty()) {
glsafe(::glGenBuffers(1, &this->m_contour_EBO_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_contour_EBO_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
this->contour_indices.clear();
}
}
void GLPaintContour::release_geometry()
{
if (this->m_contour_VBO_id) {
glsafe(::glDeleteBuffers(1, &this->m_contour_VBO_id));
this->m_contour_VBO_id = 0;
}
if (this->m_contour_EBO_id) {
glsafe(::glDeleteBuffers(1, &this->m_contour_EBO_id));
this->m_contour_EBO_id = 0;
}
this->clear();
}
#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui)

View File

@ -10,6 +10,7 @@
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include <cereal/types/vector.hpp> #include <cereal/types/vector.hpp>
#include <GL/glew.h>
@ -26,6 +27,41 @@ enum class PainterGizmoType {
MMU_SEGMENTATION MMU_SEGMENTATION
}; };
class GLPaintContour
{
public:
GLPaintContour() = default;
void render() const;
inline bool has_VBO() const { return this->m_contour_EBO_id != 0; }
// Release the geometry data, release OpenGL VBOs.
void release_geometry();
// Finalize the initialization of the contour geometry and the indices, upload both to OpenGL VBO objects
// and possibly releasing it if it has been loaded into the VBOs.
void finalize_geometry();
void clear()
{
this->contour_vertices.clear();
this->contour_indices.clear();
this->contour_indices_size = 0;
}
std::vector<float> contour_vertices;
std::vector<int> contour_indices;
// When the triangle indices are loaded into the graphics card as Vertex Buffer Objects,
// the above mentioned std::vectors are cleared and the following variables keep their original length.
size_t contour_indices_size{0};
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
// Zero if the VBOs are not sent to GPU yet.
GLuint m_contour_VBO_id{0};
GLuint m_contour_EBO_id{0};
};
class TriangleSelectorGUI : public TriangleSelector { class TriangleSelectorGUI : public TriangleSelector {
public: public:
@ -49,12 +85,18 @@ public:
protected: protected:
bool m_update_render_data = false; bool m_update_render_data = false;
static std::array<float, 4> get_seed_fill_color(const std::array<float, 4> &base_color);
private: private:
void update_render_data(); void update_render_data();
GLIndexedVertexArray m_iva_enforcers; GLIndexedVertexArray m_iva_enforcers;
GLIndexedVertexArray m_iva_blockers; GLIndexedVertexArray m_iva_blockers;
std::array<GLIndexedVertexArray, 3> m_iva_seed_fills;
std::array<GLIndexedVertexArray, 3> m_varrays; std::array<GLIndexedVertexArray, 3> m_varrays;
protected:
GLPaintContour m_paint_contour;
}; };
class GLGizmoTransparentRender class GLGizmoTransparentRender

View File

@ -77,7 +77,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
if (! m_c->selection_info()->model_object()) if (! m_c->selection_info()->model_object())
return; return;
const float approx_height = m_imgui->scaled(14.0f); const float approx_height = m_imgui->scaled(12.5f);
y = std::min(y, bottom_limit - approx_height); y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
@ -87,27 +87,28 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
m_imgui->calc_text_size(m_desc.at("reset_direction")).x) m_imgui->calc_text_size(m_desc.at("reset_direction")).x)
+ m_imgui->scaled(1.5f); + m_imgui->scaled(1.5f);
const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).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 const float cursor_type_radio_left = m_imgui->calc_text_size(m_desc["cursor_type"]).x + m_imgui->scaled(1.f);
+ 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_width2 = m_imgui->calc_text_size(m_desc["sphere"]).x const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
+ 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 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); const float minimal_slider_width = m_imgui->scaled(4.f);
float caption_max = 0.f; float caption_max = 0.f;
float total_text_max = 0.f; float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) { for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t + "_caption")).x); caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
} }
caption_max += m_imgui->scaled(1.f); total_text_max += caption_max + m_imgui->scaled(1.f);
total_text_max += m_imgui->scaled(1.f); caption_max += m_imgui->scaled(1.f);
float window_width = minimal_slider_width + std::max(cursor_size_slider_left, clipping_slider_left); float sliders_width = std::max(cursor_size_slider_left, clipping_slider_left);
float window_width = minimal_slider_width + sliders_width;
window_width = std::max(window_width, total_text_max); window_width = std::max(window_width, total_text_max);
window_width = std::max(window_width, button_width); 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, cursor_type_radio_left + cursor_type_radio_sphere + cursor_type_radio_circle);
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { 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); static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
@ -119,32 +120,15 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"}) for (const auto &t : std::array<std::string, 3>{"enforce", "block", "remove"})
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
m_imgui->text(""); ImGui::Separator();
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"),
UndoRedo::SnapshotType::GizmoAction);
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();
m_triangle_selectors[idx]->request_update_render_data();
}
}
update_model_object();
m_parent.set_as_dirty();
}
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size")); m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(cursor_size_slider_left); ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - cursor_size_slider_left); ImGui::PushItemWidth(window_width - sliders_width);
m_imgui->slider_float(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width); ImGui::PushTextWrapPos(max_tooltip_width);
@ -155,12 +139,12 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_type")); 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; float cursor_type_offset = cursor_type_radio_left + (window_width - cursor_type_radio_left - cursor_type_radio_sphere - cursor_type_radio_circle + m_imgui->scaled(0.5f)) / 2.f;
if (m_imgui->radio_button(m_desc["sphere"], sphere_sel)) ImGui::SameLine(cursor_type_offset);
sphere_sel = true; ImGui::PushItemWidth(cursor_type_radio_sphere);
if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE))
m_cursor_type = TriangleSelector::CursorType::SPHERE;
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
@ -170,11 +154,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::SameLine(cursor_type_radio_left + cursor_type_radio_width2 + m_imgui->scaled(0.f)); ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere);
ImGui::PushItemWidth(cursor_type_radio_width2); ImGui::PushItemWidth(cursor_type_radio_circle);
if (m_imgui->radio_button(m_desc["circle"], m_cursor_type == TriangleSelector::CursorType::CIRCLE))
if (m_imgui->radio_button(m_desc["circle"], ! sphere_sel)) m_cursor_type = TriangleSelector::CursorType::CIRCLE;
sphere_sel = false;
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
@ -184,12 +167,6 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
m_cursor_type = sphere_sel
? TriangleSelector::CursorType::SPHERE
: TriangleSelector::CursorType::CIRCLE;
ImGui::Separator(); ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f) { if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
@ -203,10 +180,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
} }
} }
ImGui::SameLine(clipping_slider_left); ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - clipping_slider_left); ImGui::PushItemWidth(window_width - sliders_width);
auto clp_dist = float(m_c->object_clipper()->get_position()); auto clp_dist = float(m_c->object_clipper()->get_position());
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true); m_c->object_clipper()->set_position(clp_dist, true);
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
@ -217,6 +194,22 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::Separator();
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"), UndoRedo::SnapshotType::GizmoAction);
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();
m_triangle_selectors[idx]->request_update_render_data();
}
update_model_object();
m_parent.set_as_dirty();
}
m_imgui->end(); m_imgui->end();
} }

View File

@ -548,7 +548,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
// mouse anywhere // mouse anywhere
if (evt.Moving()) { if (evt.Moving()) {
m_tooltip = update_hover_state(mouse_pos); m_tooltip = update_hover_state(mouse_pos);
if (m_current == MmuSegmentation) if (m_current == MmuSegmentation || m_current == FdmSupports)
gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown()); gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown());
} else if (evt.LeftUp()) { } else if (evt.LeftUp()) {
if (m_mouse_capture.left) { if (m_mouse_capture.left) {