mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-05-09 18:19:10 +08:00
Added simple autosetting of custom supports based on facet normal angle
This commit is contained in:
parent
e0b04e7d36
commit
d24a3453af
@ -523,6 +523,46 @@ void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwrite, bool block)
|
||||||
|
{
|
||||||
|
float threshold = (M_PI/180.)*threshold_deg;
|
||||||
|
const Selection& selection = m_parent.get_selection();
|
||||||
|
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||||
|
const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
|
||||||
|
|
||||||
|
int mesh_id = -1;
|
||||||
|
for (const ModelVolume* mv : mo->volumes) {
|
||||||
|
if (! mv->is_model_part())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
++mesh_id;
|
||||||
|
|
||||||
|
const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true);
|
||||||
|
Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast<float>().normalized();
|
||||||
|
Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast<float>().normalized();
|
||||||
|
|
||||||
|
float dot_limit = limit.dot(down);
|
||||||
|
|
||||||
|
// Now calculate dot product of vert_direction and facets' normals.
|
||||||
|
int idx = -1;
|
||||||
|
for (const stl_facet& facet : mv->mesh().stl.facet_start) {
|
||||||
|
++idx;
|
||||||
|
if (facet.normal.dot(down) > dot_limit && (overwrite || m_selected_facets[mesh_id][idx] == FacetSupportType::NONE))
|
||||||
|
m_selected_facets[mesh_id][idx] = block
|
||||||
|
? FacetSupportType::BLOCKER
|
||||||
|
: FacetSupportType::ENFORCER;
|
||||||
|
}
|
||||||
|
update_vertex_buffers(mv, mesh_id, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle")
|
||||||
|
: _L("Add supports by angle"));
|
||||||
|
update_model_object();
|
||||||
|
m_parent.set_as_dirty();
|
||||||
|
m_setting_angle = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit)
|
void GLGizmoFdmSupports::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())
|
||||||
@ -531,96 +571,124 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||||||
const float approx_height = m_imgui->scaled(18.0f);
|
const float approx_height = m_imgui->scaled(18.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);
|
||||||
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:
|
if (! m_setting_angle) {
|
||||||
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);
|
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||||
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;
|
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||||
float total_text_max = 0.;
|
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);
|
||||||
for (const std::string& t : {"enforce", "block", "remove"}) {
|
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||||
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x);
|
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
||||||
total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x);
|
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||||
}
|
|
||||||
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);
|
float caption_max = 0.f;
|
||||||
window_width = std::max(window_width, total_text_max);
|
float total_text_max = 0.;
|
||||||
window_width = std::max(window_width, button_width);
|
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);
|
||||||
|
|
||||||
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
|
float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
|
||||||
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
|
window_width = std::max(window_width, total_text_max);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
|
window_width = std::max(window_width, button_width);
|
||||||
m_imgui->text(caption);
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::SameLine(caption_max);
|
|
||||||
m_imgui->text(text);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const std::string& t : {"enforce", "block", "remove"})
|
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
|
||||||
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
|
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
|
||||||
|
m_imgui->text(caption);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::SameLine(caption_max);
|
||||||
|
m_imgui->text(text);
|
||||||
|
};
|
||||||
|
|
||||||
m_imgui->text("");
|
for (const std::string& t : {"enforce", "block", "remove"})
|
||||||
|
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
|
||||||
|
|
||||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
m_imgui->text("");
|
||||||
ModelObject* mo = m_c->selection_info()->model_object();
|
|
||||||
int idx = -1;
|
if (m_imgui->button("Autoset by angle...")) {
|
||||||
for (ModelVolume* mv : mo->volumes) {
|
m_setting_angle = true;
|
||||||
++idx;
|
}
|
||||||
if (mv->is_model_part()) {
|
|
||||||
m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE);
|
ImGui::SameLine();
|
||||||
mv->m_supported_facets.clear();
|
|
||||||
update_vertex_buffers(mv, idx, true, true);
|
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||||
m_parent.set_as_dirty();
|
ModelObject* mo = m_c->selection_info()->model_object();
|
||||||
|
int idx = -1;
|
||||||
|
for (ModelVolume* mv : mo->volumes) {
|
||||||
|
++idx;
|
||||||
|
if (mv->is_model_part()) {
|
||||||
|
m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE);
|
||||||
|
mv->m_supported_facets.clear();
|
||||||
|
update_vertex_buffers(mv, idx, true, true);
|
||||||
|
m_parent.set_as_dirty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||||
|
|
||||||
m_imgui->text(m_desc.at("cursor_size"));
|
m_imgui->text(m_desc.at("cursor_size"));
|
||||||
ImGui::SameLine(clipping_slider_left);
|
ImGui::SameLine(clipping_slider_left);
|
||||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||||
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
ImGui::SliderFloat(" ", &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);
|
||||||
ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
|
ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
|
||||||
ImGui::PopTextWrapPos();
|
ImGui::PopTextWrapPos();
|
||||||
ImGui::EndTooltip();
|
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::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();
|
||||||
|
if (m_setting_angle)
|
||||||
|
m_parent.set_as_dirty();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
ImGui::SameLine(clipping_slider_left);
|
std::string name = "Autoset custom supports";
|
||||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
m_imgui->begin(wxString(name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||||
float clp_dist = m_c->object_clipper()->get_position();
|
m_imgui->text("Threshold:");
|
||||||
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
|
ImGui::SameLine();
|
||||||
m_c->object_clipper()->set_position(clp_dist, true);
|
m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f");
|
||||||
if (ImGui::IsItemHovered()) {
|
m_imgui->checkbox(wxString("Overwrite already selected facets"), m_overwrite_selected);
|
||||||
ImGui::BeginTooltip();
|
if (m_imgui->button("Enforce"))
|
||||||
ImGui::PushTextWrapPos(max_tooltip_width);
|
select_facets_by_angle(m_angle_threshold_deg, m_overwrite_selected, false);
|
||||||
ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data());
|
ImGui::SameLine();
|
||||||
ImGui::PopTextWrapPos();
|
if (m_imgui->button("Block"))
|
||||||
ImGui::EndTooltip();
|
select_facets_by_angle(m_angle_threshold_deg, m_overwrite_selected, true);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (m_imgui->button("Cancel"))
|
||||||
|
m_setting_angle = false;
|
||||||
|
m_imgui->end();
|
||||||
|
if (! m_setting_angle)
|
||||||
|
m_parent.set_as_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
m_imgui->end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLGizmoFdmSupports::on_is_activable() const
|
bool GLGizmoFdmSupports::on_is_activable() const
|
||||||
@ -680,6 +748,7 @@ void GLGizmoFdmSupports::on_set_state()
|
|||||||
}
|
}
|
||||||
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
|
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
|
||||||
// we are actually shutting down
|
// we are actually shutting down
|
||||||
|
m_setting_angle = false;
|
||||||
wxGetApp().plater()->leave_gizmos_stack();
|
wxGetApp().plater()->leave_gizmos_stack();
|
||||||
{
|
{
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off")));
|
||||||
|
@ -61,10 +61,15 @@ private:
|
|||||||
void update_model_object() const;
|
void update_model_object() const;
|
||||||
void update_from_model_object();
|
void update_from_model_object();
|
||||||
|
|
||||||
|
void select_facets_by_angle(float threshold, bool overwrite, bool block);
|
||||||
|
bool m_overwrite_selected = false;
|
||||||
|
float m_angle_threshold_deg = 45.f;
|
||||||
|
|
||||||
bool is_mesh_point_clipped(const Vec3d& point) const;
|
bool is_mesh_point_clipped(const Vec3d& point) const;
|
||||||
|
|
||||||
float m_clipping_plane_distance = 0.f;
|
float m_clipping_plane_distance = 0.f;
|
||||||
std::unique_ptr<ClippingPlane> m_clipping_plane;
|
std::unique_ptr<ClippingPlane> m_clipping_plane;
|
||||||
|
bool m_setting_angle = false;
|
||||||
|
|
||||||
// This map holds all translated description texts, so they can be easily referenced during layout calculations
|
// 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.
|
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user