mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-13 20:46:01 +08:00
SPE-2003: Several improvements to multi-material painting gizmo.
This commit is contained in:
parent
72f47f7963
commit
17c3e31f72
@ -110,6 +110,12 @@ bool GLGizmoMmuSegmentation::on_init()
|
||||
m_desc["second_color"] = _u8L("Second color");
|
||||
m_desc["remove_caption"] = _u8L("Shift + Left mouse button") + ": ";
|
||||
m_desc["remove"] = _u8L("Remove painted color");
|
||||
|
||||
m_desc["alt_caption"] = _u8L("Alt + Mouse wheel") + ": ";
|
||||
m_desc["alt_brush"] = _u8L("Change brush size");
|
||||
m_desc["alt_fill"] = _u8L("Change angle");
|
||||
m_desc["alt_height_range"] = _u8L("Change height range");
|
||||
|
||||
m_desc["remove_all"] = _u8L("Clear all");
|
||||
m_desc["circle"] = _u8L("Circle");
|
||||
m_desc["sphere"] = _u8L("Sphere");
|
||||
@ -122,10 +128,7 @@ bool GLGizmoMmuSegmentation::on_init()
|
||||
m_desc["tool_height_range"] = _u8L("Height range");
|
||||
|
||||
m_desc["smart_fill_angle"] = _u8L("Smart fill angle");
|
||||
m_desc["smart_fill_gap_area"] = _u8L("Smart fill gap");
|
||||
|
||||
m_desc["bucket_fill_angle"] = _u8L("Bucket fill angle");
|
||||
m_desc["bucket_fill_gap_area"] = _u8L("Bucket fill gap");
|
||||
|
||||
m_desc["split_triangles"] = _u8L("Split triangles");
|
||||
|
||||
@ -282,6 +285,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
ImGuiPureWrap::calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float cursor_slider_left = ImGuiPureWrap::calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float smart_fill_slider_left = ImGuiPureWrap::calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.f);
|
||||
const float bucket_fill_slider_left = ImGuiPureWrap::calc_text_size(m_desc.at("bucket_fill_angle")).x + m_imgui->scaled(1.f);
|
||||
const float height_range_slider_left = ImGuiPureWrap::calc_text_size(m_desc.at("height_range_z_range")).x + m_imgui->scaled(1.f);
|
||||
|
||||
const float cursor_type_radio_circle = ImGuiPureWrap::calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
|
||||
@ -307,15 +311,19 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
const float split_triangles_checkbox_width = ImGuiPureWrap::calc_text_size(m_desc["split_triangles"]).x + m_imgui->scaled(2.5f);
|
||||
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.f;
|
||||
for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"}) {
|
||||
for (const std::string t : {"first_color", "second_color", "remove", "alt"}) {
|
||||
caption_max = std::max(caption_max, ImGuiPureWrap::calc_text_size(m_desc[t + "_caption"]).x);
|
||||
}
|
||||
|
||||
float total_text_max = 0.f;
|
||||
for (const std::string t : {"first_color", "second_color", "remove", "alt_brush", "alt_fill", "alt_height_range"}) {
|
||||
total_text_max = std::max(total_text_max, ImGuiPureWrap::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 sliders_left_width = std::max({smart_fill_slider_left, cursor_slider_left, clipping_slider_left, height_range_slider_left});
|
||||
const float sliders_left_width = std::max({smart_fill_slider_left, bucket_fill_slider_left, cursor_slider_left, clipping_slider_left, height_range_slider_left});
|
||||
const float slider_icon_width = ImGuiPureWrap::get_slider_icon_size().x;
|
||||
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
|
||||
window_width = std::max(window_width, total_text_max);
|
||||
@ -331,8 +339,14 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
ImGuiPureWrap::text(text);
|
||||
};
|
||||
|
||||
for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"})
|
||||
for (const std::string t : {"first_color", "second_color", "remove"}) {
|
||||
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
|
||||
}
|
||||
|
||||
std::string alt_hint_text = (m_tool_type == ToolType::BRUSH) ? "alt_brush" :
|
||||
(m_tool_type == ToolType::HEIGHT_RANGE) ? "alt_height_range"
|
||||
: "alt_fill";
|
||||
draw_text_with_caption(m_desc.at("alt_caption"), m_desc.at(alt_hint_text));
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
@ -486,18 +500,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
"placed after the number with no whitespace in between.");
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
|
||||
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str_angle.data(), 1.0f, true, _L("Alt + Mouse wheel"))) {
|
||||
for (auto &triangle_selector: m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGuiPureWrap::text((m_tool_type == ToolType::SMART_FILL ? m_desc["smart_fill_gap_area"] : m_desc["bucket_fill_gap_area"]) + ":");
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
|
||||
if (m_imgui->slider_float("##smart_fill_gap_area", &m_smart_fill_gap_area, SmartFillGapAreaMin, SmartFillGapAreaMax, "%.2f", 1.0f, true)) {
|
||||
float &fill_angle = (m_tool_type == ToolType::SMART_FILL) ? m_smart_fill_angle : m_bucket_fill_angle;
|
||||
if (m_imgui->slider_float("##fill_angle", &fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str_angle.data(), 1.0f, true, _L("Alt + Mouse wheel"))) {
|
||||
for (auto &triangle_selector: m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
|
@ -541,8 +541,10 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
m_parent.set_as_dirty();
|
||||
return true;
|
||||
} else if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL) {
|
||||
m_smart_fill_angle = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_smart_fill_angle - SmartFillAngleStep, SmartFillAngleMin)
|
||||
: std::min(m_smart_fill_angle + SmartFillAngleStep, SmartFillAngleMax);
|
||||
float &fill_angle = (m_tool_type == ToolType::SMART_FILL) ? m_smart_fill_angle : m_bucket_fill_angle;
|
||||
fill_angle = (action == SLAGizmoEventType::MouseWheelDown) ? std::max(fill_angle - SmartFillAngleStep, SmartFillAngleMin)
|
||||
: std::min(fill_angle + SmartFillAngleStep, SmartFillAngleMax);
|
||||
|
||||
m_parent.set_as_dirty();
|
||||
if (m_rr.mesh_id != -1) {
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
@ -553,11 +555,11 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
|
||||
if (m_tool_type == ToolType::SMART_FILL) {
|
||||
const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix_no_offset() * mo->volumes[m_rr.mesh_id]->get_matrix_no_offset();
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clipping_plane, m_smart_fill_angle, m_smart_fill_gap_area,
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clipping_plane, m_smart_fill_angle, SmartFillGapArea,
|
||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, TriangleSelector::ForceReselection::YES);
|
||||
} else {
|
||||
assert(m_tool_type == ToolType::BUCKET_FILL);
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clipping_plane, m_smart_fill_angle, m_smart_fill_gap_area,
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clipping_plane, m_bucket_fill_angle, BucketFillGapArea,
|
||||
TriangleSelector::BucketFillPropagate::YES, TriangleSelector::ForceReselection::YES);
|
||||
}
|
||||
|
||||
@ -649,12 +651,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state);
|
||||
|
||||
if (m_tool_type == ToolType::SMART_FILL) {
|
||||
m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle, m_smart_fill_gap_area,
|
||||
m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle, SmartFillGapArea,
|
||||
(m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f), TriangleSelector::ForceReselection::YES);
|
||||
} else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) {
|
||||
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, m_smart_fill_angle, m_smart_fill_gap_area, TriangleSelector::BucketFillPropagate::NO, TriangleSelector::ForceReselection::YES);
|
||||
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, m_bucket_fill_angle, BucketFillGapArea, TriangleSelector::BucketFillPropagate::NO, TriangleSelector::ForceReselection::YES);
|
||||
} else if (m_tool_type == ToolType::BUCKET_FILL) {
|
||||
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, m_smart_fill_angle, m_smart_fill_gap_area, TriangleSelector::BucketFillPropagate::YES, TriangleSelector::ForceReselection::YES);
|
||||
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, m_bucket_fill_angle, BucketFillGapArea, TriangleSelector::BucketFillPropagate::YES, TriangleSelector::ForceReselection::YES);
|
||||
}
|
||||
|
||||
m_seed_fill_last_mesh_id = -1;
|
||||
@ -744,12 +746,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
|
||||
if (m_tool_type == ToolType::SMART_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle, m_smart_fill_gap_area,
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle, SmartFillGapArea,
|
||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, m_smart_fill_angle, m_smart_fill_gap_area, TriangleSelector::BucketFillPropagate::NO);
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, m_bucket_fill_angle, BucketFillGapArea, TriangleSelector::BucketFillPropagate::NO);
|
||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, m_smart_fill_angle, m_smart_fill_gap_area, TriangleSelector::BucketFillPropagate::YES);
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, m_bucket_fill_angle, BucketFillGapArea, TriangleSelector::BucketFillPropagate::YES);
|
||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
||||
return true;
|
||||
|
@ -154,7 +154,7 @@ protected:
|
||||
bool m_triangle_splitting_enabled = true;
|
||||
ToolType m_tool_type = ToolType::BRUSH;
|
||||
float m_smart_fill_angle = 30.f;
|
||||
float m_smart_fill_gap_area = 0.02f;
|
||||
float m_bucket_fill_angle = 90.f;
|
||||
float m_height_range_z_range = 1.00f;
|
||||
|
||||
bool m_paint_on_overhangs_only = false;
|
||||
@ -168,13 +168,13 @@ protected:
|
||||
static constexpr float SmartFillAngleMax = 90.f;
|
||||
static constexpr float SmartFillAngleStep = 1.f;
|
||||
|
||||
static constexpr float SmartFillGapAreaMin = 0.0f;
|
||||
static constexpr float SmartFillGapAreaMax = 1.f;
|
||||
|
||||
static constexpr float HeightRangeZRangeMin = 0.1f;
|
||||
static constexpr float HeightRangeZRangeMax = 10.f;
|
||||
static constexpr float HeightRangeZRangeStep = 0.1f;
|
||||
|
||||
static constexpr float SmartFillGapArea = 0.02f;
|
||||
static constexpr float BucketFillGapArea = 0.02f;
|
||||
|
||||
// It stores the value of the previous mesh_id to which the seed fill was applied.
|
||||
// It is used to detect when the mouse has moved from one volume to another one.
|
||||
int m_seed_fill_last_mesh_id = -1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user