Merge branch 'lm_fdm_custom_supports_polishing'
This commit is contained in:
commit
3a6bc2e31a
11 changed files with 307 additions and 144 deletions
|
@ -411,6 +411,11 @@ public:
|
|||
return timestamp == other.get_timestamp();
|
||||
}
|
||||
|
||||
template<class Archive> void serialize(Archive &ar)
|
||||
{
|
||||
ar(m_data);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<int, FacetSupportType> m_data;
|
||||
|
||||
|
@ -613,7 +618,8 @@ private:
|
|||
}
|
||||
template<class Archive> void load(Archive &ar) {
|
||||
bool has_convex_hull;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
|
||||
m_is_splittable, has_convex_hull, m_supported_facets);
|
||||
cereal::load_by_value(ar, config);
|
||||
assert(m_mesh);
|
||||
if (has_convex_hull) {
|
||||
|
@ -626,7 +632,8 @@ private:
|
|||
}
|
||||
template<class Archive> void save(Archive &ar) const {
|
||||
bool has_convex_hull = m_convex_hull.get() != nullptr;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
|
||||
m_is_splittable, has_convex_hull, m_supported_facets);
|
||||
cereal::save_by_value(ar, config);
|
||||
if (has_convex_hull)
|
||||
cereal::save_optional(ar, m_convex_hull);
|
||||
|
|
|
@ -1434,7 +1434,7 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas
|
|||
#if ENABLE_SLOPE_RENDERING
|
||||
void GLCanvas3D::Slope::render() const
|
||||
{
|
||||
if (is_shown())
|
||||
if (m_dialog_shown)
|
||||
{
|
||||
const std::array<float, 2>& z_range = m_volumes.get_slope_z_range();
|
||||
std::array<float, 2> angle_range = { Geometry::rad2deg(::acos(z_range[0])) - 90.0f, Geometry::rad2deg(::acos(z_range[1])) - 90.0f };
|
||||
|
@ -1445,29 +1445,41 @@ void GLCanvas3D::Slope::render() const
|
|||
imgui.set_next_window_pos((float)cnv_size.get_width(), (float)cnv_size.get_height(), ImGuiCond_Always, 1.0f, 1.0f);
|
||||
imgui.begin(_(L("Slope visualization")), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
imgui.text(_(L("Facets' normal angle range (degrees)")) + ":");
|
||||
imgui.text(_(L("Facets' slope range (degrees)")) + ":");
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.75f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 1.0f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.85f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.25f, 0.0f, 1.0f));
|
||||
if (ImGui::SliderFloat("##yellow", &angle_range[0], 0.0f, 90.0f, "%.1f"))
|
||||
{
|
||||
modified = true;
|
||||
if (angle_range[1] < angle_range[0])
|
||||
angle_range[1] = angle_range[0];
|
||||
}
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.0f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 0.0f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.0f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.0f, 0.0f, 1.0f));
|
||||
if (ImGui::SliderFloat("##red", &angle_range[1], 0.0f, 90.0f, "%.1f"))
|
||||
|
||||
// angle_range is range of normal angle, GUI should
|
||||
// show facet slope angle
|
||||
float slope_bound = 90.f - angle_range[1];
|
||||
bool mod = ImGui::SliderFloat("##red", &slope_bound, 0.0f, 90.0f, "%.1f");
|
||||
angle_range[1] = 90.f - slope_bound;
|
||||
if (mod)
|
||||
{
|
||||
modified = true;
|
||||
if (angle_range[0] > angle_range[1])
|
||||
angle_range[0] = angle_range[1];
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.75f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 1.0f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.85f, 0.0f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.25f, 0.0f, 1.0f));
|
||||
|
||||
slope_bound = 90.f - angle_range[0];
|
||||
mod = ImGui::SliderFloat("##yellow", &slope_bound, 0.0f, 90.0f, "%.1f");
|
||||
angle_range[0] = 90.f - slope_bound;
|
||||
if (mod)
|
||||
{
|
||||
modified = true;
|
||||
if (angle_range[1] < angle_range[0])
|
||||
angle_range[1] = angle_range[0];
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
|
||||
ImGui::Separator();
|
||||
|
@ -1482,7 +1494,7 @@ void GLCanvas3D::Slope::render() const
|
|||
imgui.end();
|
||||
|
||||
if (modified)
|
||||
m_volumes.set_slope_z_range({ -::cos(Geometry::deg2rad(90.0f - angle_range[0])), -::cos(Geometry::deg2rad(90.0f - angle_range[1])) });
|
||||
set_range(angle_range);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
@ -1854,8 +1866,8 @@ bool GLCanvas3D::is_reload_delayed() const
|
|||
void GLCanvas3D::enable_layers_editing(bool enable)
|
||||
{
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
if (enable && m_slope.is_shown())
|
||||
m_slope.show(false);
|
||||
if (enable && m_slope.is_dialog_shown())
|
||||
m_slope.show_dialog(false);
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
m_layers_editing.set_enabled(enable);
|
||||
|
@ -3019,7 +3031,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
case 'd': {
|
||||
if (!is_layers_editing_enabled())
|
||||
{
|
||||
m_slope.show(!m_slope.is_shown());
|
||||
m_slope.show_dialog(!m_slope.is_dialog_shown());
|
||||
m_dirty = true;
|
||||
}
|
||||
break;
|
||||
|
@ -3589,7 +3601,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled)
|
||||
{
|
||||
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)
|
||||
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports
|
||||
&& m_gizmos.get_current_type() != GLGizmosManager::FdmSupports)
|
||||
{
|
||||
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
|
||||
m_dirty = true;
|
||||
|
|
|
@ -409,6 +409,7 @@ private:
|
|||
class Slope
|
||||
{
|
||||
bool m_enabled{ false };
|
||||
bool m_dialog_shown{ false };
|
||||
GLCanvas3D& m_canvas;
|
||||
GLVolumeCollection& m_volumes;
|
||||
|
||||
|
@ -417,9 +418,14 @@ private:
|
|||
|
||||
void enable(bool enable) { m_enabled = enable; }
|
||||
bool is_enabled() const { return m_enabled; }
|
||||
void show(bool show) { m_volumes.set_slope_active(m_enabled ? show : false); }
|
||||
bool is_shown() const { return m_volumes.is_slope_active(); }
|
||||
void use(bool use) { m_volumes.set_slope_active(m_enabled ? use : false); }
|
||||
bool is_used() const { return m_volumes.is_slope_active(); }
|
||||
void show_dialog(bool show) { if (show && is_used()) return; use(show); m_dialog_shown = show; }
|
||||
bool is_dialog_shown() const { return m_dialog_shown; }
|
||||
void render() const;
|
||||
void set_range(const std::array<float, 2>& range) const {
|
||||
m_volumes.set_slope_z_range({ -::cos(Geometry::deg2rad(90.0f - range[0])), -::cos(Geometry::deg2rad(90.0f - range[1])) });
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
|
@ -713,8 +719,10 @@ public:
|
|||
void show_labels(bool show) { m_labels.show(show); }
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
bool is_slope_shown() const { return m_slope.is_shown(); }
|
||||
void show_slope(bool show) { m_slope.show(show); }
|
||||
bool is_slope_shown() const { return m_slope.is_dialog_shown(); }
|
||||
void use_slope(bool use) { m_slope.use(use); }
|
||||
void show_slope(bool show) { m_slope.show_dialog(show); }
|
||||
void set_slope_range(const std::array<float, 2>& range) { m_slope.set_range(range); }
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
|
||||
private:
|
||||
|
|
|
@ -2771,6 +2771,7 @@ void ObjectList::delete_all_objects_from_list()
|
|||
void ObjectList::increase_object_instances(const size_t obj_idx, const size_t num)
|
||||
{
|
||||
select_item(m_objects_model->AddInstanceChild(m_objects_model->GetItemById(obj_idx), num));
|
||||
selection_changed();
|
||||
}
|
||||
|
||||
void ObjectList::decrease_object_instances(const size_t obj_idx, const size_t num)
|
||||
|
|
|
@ -58,10 +58,10 @@ void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const S
|
|||
return;
|
||||
|
||||
if (mo && selection.is_from_single_instance()
|
||||
&& (mo != m_old_mo || mo->volumes.size() != m_old_volumes_size))
|
||||
&& (mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size))
|
||||
{
|
||||
update_mesh();
|
||||
m_old_mo = mo;
|
||||
update_from_model_object();
|
||||
m_old_mo_id = mo->id();
|
||||
m_old_volumes_size = mo->volumes.size();
|
||||
}
|
||||
}
|
||||
|
@ -84,12 +84,29 @@ void GLGizmoFdmSupports::on_render() const
|
|||
|
||||
void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
|
||||
{
|
||||
if (m_setting_angle)
|
||||
return;
|
||||
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } );
|
||||
glsafe(::glPolygonOffset(-1.0, 1.0));
|
||||
|
||||
// Take care of the clipping plane. The normal of the clipping plane is
|
||||
// saved with opposite sign than we need to pass to OpenGL (FIXME)
|
||||
bool clipping_plane_active = m_c->object_clipper()->get_position() != 0.;
|
||||
if (clipping_plane_active) {
|
||||
const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
|
||||
double clp_data[4];
|
||||
memcpy(clp_data, clp->get_data(), 4 * sizeof(double));
|
||||
for (int i=0; i<3; ++i)
|
||||
clp_data[i] = -1. * clp_data[i];
|
||||
|
||||
glsafe(::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)clp_data));
|
||||
glsafe(::glEnable(GL_CLIP_PLANE0));
|
||||
}
|
||||
|
||||
int mesh_id = -1;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (! mv->is_model_part())
|
||||
|
@ -113,6 +130,8 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
|
|||
}
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
if (clipping_plane_active)
|
||||
glsafe(::glDisable(GL_CLIP_PLANE0));
|
||||
}
|
||||
|
||||
|
||||
|
@ -161,14 +180,21 @@ void GLGizmoFdmSupports::render_cursor_circle() const
|
|||
}
|
||||
|
||||
|
||||
void GLGizmoFdmSupports::on_render_for_picking() const
|
||||
void GLGizmoFdmSupports::update_model_object() const
|
||||
{
|
||||
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
int idx = -1;
|
||||
for (ModelVolume* mv : mo->volumes) {
|
||||
++idx;
|
||||
if (! mv->is_model_part())
|
||||
continue;
|
||||
for (int i=0; i<int(m_selected_facets[idx].size()); ++i)
|
||||
mv->m_supported_facets.set_facet(i, m_selected_facets[idx][i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoFdmSupports::update_mesh()
|
||||
void GLGizmoFdmSupports::update_from_model_object()
|
||||
{
|
||||
wxBusyCursor wait;
|
||||
|
||||
|
@ -177,7 +203,6 @@ void GLGizmoFdmSupports::update_mesh()
|
|||
for (const ModelVolume* mv : mo->volumes)
|
||||
if (mv->is_model_part())
|
||||
++num_of_volumes;
|
||||
|
||||
m_selected_facets.resize(num_of_volumes);
|
||||
m_neighbors.resize(num_of_volumes);
|
||||
m_ivas.clear();
|
||||
|
@ -218,6 +243,21 @@ void GLGizmoFdmSupports::update_mesh()
|
|||
|
||||
|
||||
|
||||
bool GLGizmoFdmSupports::is_mesh_point_clipped(const Vec3d& point) const
|
||||
{
|
||||
if (m_c->object_clipper()->get_position() == 0.)
|
||||
return false;
|
||||
|
||||
auto sel_info = m_c->selection_info();
|
||||
int active_inst = m_c->selection_info()->get_active_instance();
|
||||
const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
|
||||
const Transform3d& trafo = mi->get_transformation().get_matrix();
|
||||
|
||||
Vec3d transformed_point = trafo * point;
|
||||
transformed_point(2) += sel_info->get_sla_shift();
|
||||
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
|
||||
}
|
||||
|
||||
|
||||
bool operator<(const GLGizmoFdmSupports::NeighborData& a, const GLGizmoFdmSupports::NeighborData& b) {
|
||||
return a.first < b.first;
|
||||
|
@ -310,6 +350,12 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
m_clipping_plane.get(),
|
||||
&facet))
|
||||
{
|
||||
// In case this hit is clipped, skip it.
|
||||
if (is_mesh_point_clipped(hit.cast<double>())) {
|
||||
some_mesh_was_hit = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this hit the closest to the camera so far?
|
||||
double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast<double>()).squaredNorm();
|
||||
if (hit_squared_distance < closest_hit_squared_distance) {
|
||||
|
@ -430,19 +476,16 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
|
||||
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp)
|
||||
&& m_button_down != Button::None) {
|
||||
// Take snapshot and update ModelVolume data.
|
||||
wxString action_name = shift_down
|
||||
? _L("Remove selection")
|
||||
: (m_button_down == Button::Left
|
||||
? _L("Add supports")
|
||||
: _L("Block supports"));
|
||||
Plater::TakeSnapshot(wxGetApp().plater(), action_name);
|
||||
update_model_object();
|
||||
|
||||
m_button_down = Button::None;
|
||||
|
||||
// Synchronize gizmo with ModelVolume data.
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
int idx = -1;
|
||||
for (ModelVolume* mv : mo->volumes) {
|
||||
++idx;
|
||||
if (! mv->is_model_part())
|
||||
continue;
|
||||
for (int i=0; i<int(m_selected_facets[idx].size()); ++i)
|
||||
mv->m_supported_facets.set_facet(i, m_selected_facets[idx][i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -481,6 +524,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)
|
||||
{
|
||||
if (! m_c->selection_info()->model_object())
|
||||
|
@ -489,96 +572,131 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
const float approx_height = m_imgui->scaled(18.0f);
|
||||
y = std::min(y, bottom_limit - approx_height);
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||
if (! m_setting_angle) {
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.;
|
||||
for (const std::string& t : {"enforce", "block", "remove"}) {
|
||||
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x);
|
||||
total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x);
|
||||
}
|
||||
caption_max += m_imgui->scaled(1.f);
|
||||
total_text_max += m_imgui->scaled(1.f);
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||
|
||||
float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
|
||||
window_width = std::max(window_width, total_text_max);
|
||||
window_width = std::max(window_width, button_width);
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.;
|
||||
for (const std::string& t : {"enforce", "block", "remove"}) {
|
||||
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x);
|
||||
total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x);
|
||||
}
|
||||
caption_max += m_imgui->scaled(1.f);
|
||||
total_text_max += m_imgui->scaled(1.f);
|
||||
|
||||
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);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
|
||||
m_imgui->text(caption);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(caption_max);
|
||||
m_imgui->text(text);
|
||||
};
|
||||
float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
|
||||
window_width = std::max(window_width, total_text_max);
|
||||
window_width = std::max(window_width, button_width);
|
||||
|
||||
for (const std::string& t : {"enforce", "block", "remove"})
|
||||
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
|
||||
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);
|
||||
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"))) {
|
||||
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();
|
||||
m_imgui->text("");
|
||||
|
||||
if (m_imgui->button("Autoset by angle...")) {
|
||||
m_setting_angle = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||
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"));
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(max_tooltip_width);
|
||||
ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(max_tooltip_width);
|
||||
ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
m_c->object_clipper()->set_position(-1., false);
|
||||
});
|
||||
ImGui::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.show_slope(false);
|
||||
m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg});
|
||||
m_parent.use_slope(true);
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
else {
|
||||
std::string name = "Autoset custom supports";
|
||||
m_imgui->begin(wxString(name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->text("Threshold:");
|
||||
ImGui::SameLine();
|
||||
if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f"))
|
||||
m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg});
|
||||
m_imgui->checkbox(wxString("Overwrite already selected facets"), m_overwrite_selected);
|
||||
if (m_imgui->button("Enforce"))
|
||||
select_facets_by_angle(m_angle_threshold_deg, m_overwrite_selected, false);
|
||||
ImGui::SameLine();
|
||||
if (m_imgui->button("Block"))
|
||||
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.use_slope(false);
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
m_imgui->end();
|
||||
}
|
||||
|
||||
bool GLGizmoFdmSupports::on_is_activable() const
|
||||
|
@ -627,12 +745,24 @@ void GLGizmoFdmSupports::on_set_state()
|
|||
return;
|
||||
|
||||
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned on")));
|
||||
{
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned on")));
|
||||
}
|
||||
if (! m_parent.get_gizmos_manager().is_serializing()) {
|
||||
wxGetApp().CallAfter([]() {
|
||||
wxGetApp().plater()->enter_gizmos_stack();
|
||||
});
|
||||
}
|
||||
}
|
||||
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
|
||||
// we are actually shutting down
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off")));
|
||||
m_old_mo = nullptr;
|
||||
m_setting_angle = false;
|
||||
m_parent.use_slope(false);
|
||||
wxGetApp().plater()->leave_gizmos_stack();
|
||||
{
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off")));
|
||||
}
|
||||
m_old_mo_id = -1;
|
||||
m_ivas.clear();
|
||||
m_neighbors.clear();
|
||||
m_selected_facets.clear();
|
||||
|
@ -657,7 +787,7 @@ void GLGizmoFdmSupports::on_stop_dragging()
|
|||
|
||||
void GLGizmoFdmSupports::on_load(cereal::BinaryInputArchive& ar)
|
||||
{
|
||||
|
||||
update_from_model_object();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ enum class SLAGizmoEventType : unsigned char;
|
|||
class GLGizmoFdmSupports : public GLGizmoBase
|
||||
{
|
||||
private:
|
||||
const ModelObject* m_old_mo = nullptr;
|
||||
ObjectID m_old_mo_id;
|
||||
size_t m_old_volumes_size = 0;
|
||||
|
||||
GLUquadricObj* m_quadric;
|
||||
|
@ -53,14 +53,23 @@ public:
|
|||
private:
|
||||
bool on_init() override;
|
||||
void on_render() const override;
|
||||
void on_render_for_picking() const override;
|
||||
void on_render_for_picking() const override {}
|
||||
|
||||
void render_triangles(const Selection& selection) const;
|
||||
void render_cursor_circle() const;
|
||||
void update_mesh();
|
||||
|
||||
void update_model_object() const;
|
||||
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;
|
||||
|
||||
float m_clipping_plane_distance = 0.f;
|
||||
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
|
||||
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
|
||||
|
|
|
@ -66,11 +66,10 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
|
|||
|
||||
ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
||||
if (mo != m_old_mo) {
|
||||
if (mo && mo->id() != m_old_mo_id) {
|
||||
disable_editing_mode();
|
||||
if (mo)
|
||||
reload_cache();
|
||||
m_old_mo = mo;
|
||||
reload_cache();
|
||||
m_old_mo_id = mo->id();
|
||||
}
|
||||
|
||||
// If we triggered autogeneration before, check backend and fetch results if they are there
|
||||
|
|
|
@ -83,7 +83,7 @@ private:
|
|||
float m_density_stash = 0.f; // and again
|
||||
mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selected
|
||||
std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
|
||||
const ModelObject* m_old_mo = nullptr;
|
||||
ObjectID m_old_mo_id;
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -369,15 +369,6 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object)
|
|||
|| wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
|
||||
return;
|
||||
|
||||
/*m_common_gizmos_data->update_from_backend(m_parent, model_object);
|
||||
|
||||
auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get());
|
||||
|
||||
|
||||
// note: sla support gizmo takes care of updating the common data.
|
||||
// following lines are thus dependent
|
||||
//gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection());
|
||||
*/
|
||||
auto* gizmo_hollow = dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get());
|
||||
auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get());
|
||||
gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection());
|
||||
|
|
|
@ -139,6 +139,11 @@ public:
|
|||
EType new_current = m_current;
|
||||
m_current = old_current;
|
||||
|
||||
// Update common data. They should be updated when activate_gizmo is
|
||||
// called, so it can be used in on_set_state which is called from there.
|
||||
if (new_current != Undefined)
|
||||
m_common_gizmos_data->update(m_gizmos[new_current]->get_requirements());
|
||||
|
||||
// activate_gizmo call sets m_current and calls set_state for the gizmo
|
||||
// it does nothing in case the gizmo is already activated
|
||||
// it can safely be called for Undefined gizmo
|
||||
|
@ -167,6 +172,7 @@ public:
|
|||
|
||||
void refresh_on_off_state();
|
||||
void reset_all_states();
|
||||
bool is_serializing() const { return m_serializing; }
|
||||
|
||||
void set_hover_id(int id);
|
||||
void enable_grabber(EType type, unsigned int id, bool enable);
|
||||
|
|
|
@ -4450,8 +4450,6 @@ void Plater::increase_instances(size_t num)
|
|||
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
|
||||
}
|
||||
|
||||
sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num);
|
||||
|
||||
if (p->get_config("autocenter") == "1")
|
||||
arrange();
|
||||
|
||||
|
@ -4459,8 +4457,9 @@ void Plater::increase_instances(size_t num)
|
|||
|
||||
p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1);
|
||||
|
||||
p->selection_changed();
|
||||
sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num);
|
||||
|
||||
p->selection_changed();
|
||||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue