diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index d6f20ed98..939ec7450 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -776,10 +776,19 @@ namespace Slic3r { std::vector objects; boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off); + // Info on format versioning - see 3mf.hpp + int version = 0; + if (!objects.empty() && objects[0].find("support_points_format_version=") != std::string::npos) { + objects[0].erase(objects[0].begin(), objects[0].begin() + 30); // removes the string + version = std::stoi(objects[0]); + objects.erase(objects.begin()); // pop the header + } + for (const std::string& object : objects) { std::vector object_data; boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off); + if (object_data.size() != 2) { add_error("Error while reading object data"); @@ -813,12 +822,22 @@ namespace Slic3r { std::vector sla_support_points; - for (unsigned int i=0; i #include #include "Geometry.hpp" -#include +#include namespace Slic3r { diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 256d9f3fc..c08464199 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -52,20 +52,7 @@ SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3 const Config& config, std::function throw_on_cancel) : m_config(config), m_V(emesh.V), m_F(emesh.F), m_throw_on_cancel(throw_on_cancel) { - // Find all separate islands that will need support. The coord_t number denotes height - // of a point just below the mesh (so that we can later project the point precisely - // on the mesh by raycasting (done by igl) and not risking we will place the point inside). - /*std::vector> islands = */ process(slices, heights); - - // Uniformly cover each of the islands with support points. - /*for (const auto& island : islands) { - std::vector points = uniformly_cover(island); - m_throw_on_cancel(); - project_upward_onto_mesh(points); - m_output.insert(m_output.end(), points.begin(), points.end()); - m_throw_on_cancel(); - }*/ project_onto_mesh(m_output); } @@ -104,10 +91,14 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: const ExPolygons& expolys_top = slices[i]; const float height = (i>2 ? heights[i-3] : heights[0]-(heights[1]-heights[0])); + const float layer_height = (i!=0 ? heights[i]-heights[i-1] : heights[0]); const float safe_angle = 5.f * (M_PI/180.f); // smaller number - less supports - const float offset = scale_((i!=0 ? heights[i]-heights[i-1] : heights[0]) / std::tan(safe_angle)); - const float pixel_area = 0.047f * 0.047f; // FIXME: calculate actual pixel area from printer config + const float offset = scale_(layer_height / std::tan(safe_angle)); + + // FIXME: calculate actual pixel area from printer config: + //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option("display_width") / wxGetApp().preset_bundle->project_config.option("display_pixels_x"), 2.f); // + const float pixel_area = pow(0.047f, 2.f); // Check all ExPolygons on this slice and check whether they are new or belonging to something below. for (const ExPolygon& polygon : expolys_top) { @@ -119,12 +110,11 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: const ExPolygon* bottom = s.polygon; if (polygon.overlaps(*bottom) || bottom->overlaps(polygon)) { m_structures_new.back().structures_below.push_back(&s); + coord_t centroids_dist = (bottom->contour.centroid() - polygon.contour.centroid()).norm(); - if (centroids_dist != 0) { - float mult = std::min(1.f, 1.f - std::min(1.f, 500.f * (float)(centroids_dist * centroids_dist) / (float)bottom->area())); - s.supports_force *= mult; - } - //s.supports_force *= std::min(1.f, ((float)polygon.area()/(float)bottom->area())); + float mult = std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * (float)(centroids_dist * centroids_dist) / (float)bottom->area())); + s.supports_force *= mult; + s.supports_force *= std::min(1.f, 20.f * ((float)bottom->area() / (float)polygon.area())); } } } @@ -176,7 +166,7 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: } e = diff_ex(ExPolygons{*s.polygon}, e); - s.supports_force /= std::max(1., (e_area / (s.polygon->area()*SCALING_FACTOR*SCALING_FACTOR))); + s.supports_force /= std::max(1., (layer_height / 0.3f) * (e_area / (s.polygon->area()*SCALING_FACTOR*SCALING_FACTOR))); diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index f126e3600..336f7acfa 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include // #define SLA_AUTOSUPPORTS_DEBUG diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp new file mode 100644 index 000000000..c3955bab0 --- /dev/null +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -0,0 +1,48 @@ +#ifndef SLACOMMON_HPP +#define SLACOMMON_HPP + +#include + + +namespace Slic3r { + +// Typedef from Point.hpp +typedef Eigen::Matrix Vec3f; + +namespace sla { + +struct SupportPoint { + Vec3f pos; + float head_front_radius; + bool is_new_island; + + SupportPoint() : + pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) {} + + SupportPoint(float pos_x, float pos_y, float pos_z, float head_radius, bool new_island) : + pos(pos_x, pos_y, pos_z), head_front_radius(head_radius), is_new_island(new_island) {} + + SupportPoint(Vec3f position, float head_radius, bool new_island) : + pos(position), head_front_radius(head_radius), is_new_island(new_island) {} + + SupportPoint(Eigen::Matrix data) : + pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4)) {} + + bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; } +}; + + +/// An index-triangle structure for libIGL functions. Also serves as an +/// alternative (raw) input format for the SLASupportTree +struct EigenMesh3D { + Eigen::MatrixXd V; + Eigen::MatrixXi F; + double ground_level = 0; +}; + + +} // namespace sla +} // namespace Slic3r + + +#endif // SLASUPPORTTREE_HPP \ No newline at end of file diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 2a0be806a..85202e498 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -7,6 +7,9 @@ #include #include +#include "SLACommon.hpp" + + namespace Slic3r { // Needed types from Point.hpp @@ -34,26 +37,6 @@ enum class PillarConnectionMode { dynamic }; -struct SupportPoint { - Vec3f pos; - float head_front_radius; - bool is_new_island; - - SupportPoint() : - pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) {} - - SupportPoint(float pos_x, float pos_y, float pos_z, float head_radius, bool new_island) : - pos(pos_x, pos_y, pos_z), head_front_radius(head_radius), is_new_island(new_island) {} - - SupportPoint(Vec3f position, float head_radius, bool new_island) : - pos(position), head_front_radius(head_radius), is_new_island(new_island) {} - - SupportPoint(Eigen::Matrix data) : - pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4)) {} - - bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; } -}; - struct SupportConfig { // Radius in mm of the pointing side of the head. double head_front_radius_mm = 0.2; @@ -122,14 +105,6 @@ struct Controller { std::function cancelfn = [](){}; }; -/// An index-triangle structure for libIGL functions. Also serves as an -/// alternative (raw) input format for the SLASupportTree -struct EigenMesh3D { - Eigen::MatrixXd V; - Eigen::MatrixXi F; - double ground_level = 0; -}; - using PointSet = Eigen::MatrixXd; EigenMesh3D to_eigenmesh(const TriangleMesh& m); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 30ace0e81..d25037dc1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4031,6 +4031,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_moving(false) , m_color_by("volume") , m_reload_delayed(false) + , m_render_sla_auxiliaries(true) #if !ENABLE_IMGUI , m_external_gizmo_widgets_parent(nullptr) #endif // not ENABLE_IMGUI @@ -4186,6 +4187,27 @@ int GLCanvas3D::check_volumes_outside_state() const return (int)state; } +void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible) +{ + for (GLVolume* vol : m_volumes.volumes) { + if (vol->composite_id.volume_id < 0) + vol->is_active = visible; + } + + m_render_sla_auxiliaries = visible; +} + +void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo) +{ + for (GLVolume* vol : m_volumes.volumes) { + if (mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) + vol->is_active = visible; + } + if (visible && !mo) + toggle_sla_auxiliaries_visibility(true); +} + + void GLCanvas3D::set_config(const DynamicPrintConfig* config) { m_config = config; @@ -4732,7 +4754,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (it != model_volume_state.end() && it->geometry_id == key.geometry_id) mvs = &(*it); } - if (mvs == nullptr || force_full_scene_refresh) { + if (mvs == nullptr || force_full_scene_refresh || (volume->composite_id.volume_id < 0 && !m_render_sla_auxiliaries)) { // This GLVolume will be released. if (volume->is_wipe_tower) { // There is only one wipe tower. @@ -4841,7 +4863,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volumes_count = m_volumes.volumes.size(); for (size_t istep = 0; istep < sla_steps.size(); ++istep) - if (!instances[istep].empty()) + if (!instances[istep].empty() && m_render_sla_auxiliaries) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_use_VBOs && m_initialized); } @@ -6622,6 +6644,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const unsigned int volume_id = 0; for (GLVolume* vol : m_volumes.volumes) { + if (vol->composite_id.volume_id < 0 && !m_render_sla_auxiliaries) + continue; + if (fake_colors) { // Object picking mode. Render the object with a color encoding the object index. diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9508b496e..0813170a9 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -910,6 +910,7 @@ private: bool m_multisample_allowed; bool m_regenerate_volumes; bool m_moving; + bool m_render_sla_auxiliaries; std::string m_color_by; @@ -940,6 +941,9 @@ public: void reset_volumes(); int check_volumes_outside_state() const; + void toggle_sla_auxiliaries_visibility(bool visible); + void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr); + void set_config(const DynamicPrintConfig* config); void set_process(BackgroundSlicingProcess* process); void set_model(Model* model); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index dcd99bfa6..6522287b5 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -7,7 +7,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Geometry.hpp" #include "libslic3r/Utils.hpp" -#include "libslic3r/SLA/SLASupportTree.hpp" +#include "libslic3r/SLA/SLACommon.hpp" #include "libslic3r/SLAPrint.hpp" #include @@ -1792,14 +1792,18 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { if (po->model_object()->id() == model_object->id()) { const Eigen::MatrixXd& points = po->get_support_points(); - for (unsigned int i=0; itrafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2))); - model_object->sla_support_points.emplace_back(pos(0), pos(1), pos(2), points(i, 3), points(i, 4)); - } + for (unsigned int i=0; isla_support_points.emplace_back(po->trafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)), + points(i, 3), points(i, 4)); break; } } } + m_editing_mode_cache = m_model_object->sla_support_points; // make a copy of ModelObject's support points + if (m_state == On) { + m_parent.toggle_model_objects_visibility(false); + m_parent.toggle_model_objects_visibility(true, m_model_object); + } } } @@ -1809,7 +1813,7 @@ void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const ::glEnable(GL_DEPTH_TEST); for (unsigned int i=0; isla_support_points[i].is_new_island; + bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].is_new_island; Grabber& g = m_grabbers[i]; if (m_editing_mode) { g.color[0] = supports_new_island ? 0.f : 1.f; @@ -1888,7 +1892,7 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection, ::glPushMatrix(); ::glLoadIdentity(); ::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift); - ::gluSphere(m_quadric, m_model_object->sla_support_points[i].head_front_radius, 64, 36); + ::gluSphere(m_quadric, m_editing_mode_cache[i].head_front_radius, 64, 36); ::glPopMatrix(); } @@ -1938,7 +1942,7 @@ void GLGizmoSlaSupports::update_mesh() // we'll now reload Grabbers (selection might have changed): m_grabbers.clear(); - for (const sla::SupportPoint& point : m_model_object->sla_support_points) { + for (const sla::SupportPoint& point : m_editing_mode_cache) { m_grabbers.push_back(Grabber()); m_grabbers.back().center = point.pos.cast(); } @@ -2009,7 +2013,7 @@ void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position) m_grabbers.push_back(Grabber()); m_grabbers.back().center = new_pos.cast(); - m_model_object->sla_support_points.emplace_back(new_pos, m_new_point_head_diameter, false); + m_editing_mode_cache.emplace_back(new_pos, m_new_point_head_diameter, false); // This should trigger the support generation // wxGetApp().plater()->reslice(); @@ -2024,16 +2028,16 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) if (delete_all) { m_grabbers.clear(); - m_model_object->sla_support_points.clear(); + m_editing_mode_cache.clear(); // This should trigger the support generation // wxGetApp().plater()->reslice(); } else if (m_hover_id != -1) { - if (!m_model_object->sla_support_points[m_hover_id].is_new_island || !m_lock_unique_islands) { + if (!m_editing_mode_cache[m_hover_id].is_new_island || !m_lock_unique_islands) { m_grabbers.erase(m_grabbers.begin() + m_hover_id); - m_model_object->sla_support_points.erase(m_model_object->sla_support_points.begin() + m_hover_id); + m_editing_mode_cache.erase(m_editing_mode_cache.begin() + m_hover_id); m_hover_id = -1; // This should trigger the support generation @@ -2045,15 +2049,15 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) { - if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_model_object->sla_support_points[m_hover_id].is_new_island || !m_lock_unique_islands)) { + if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].is_new_island || !m_lock_unique_islands)) { Vec3f new_pos; try { new_pos = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1))); } catch (...) { return; } m_grabbers[m_hover_id].center = new_pos.cast(); - m_model_object->sla_support_points[m_hover_id].pos = new_pos; - m_model_object->sla_support_points[m_hover_id].is_new_island = false; + m_editing_mode_cache[m_hover_id].pos = new_pos; + m_editing_mode_cache[m_hover_id].is_new_island = false; // Do not update immediately, wait until the mouse is released. // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } @@ -2102,24 +2106,32 @@ RENDER_AGAIN: m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove |/* ImGuiWindowFlags_NoResize | */ImGuiWindowFlags_NoCollapse); ImGui::PushItemWidth(100.0f); - - bool force_refresh = m_editing_mode; + + /*bool force_refresh = m_editing_mode; if (m_imgui->radio_button(_(L("Automatic")), !m_editing_mode)) m_editing_mode = false; ImGui::SameLine(); if (m_imgui->radio_button(_(L("Manual")), m_editing_mode)) m_editing_mode = true; force_refresh = force_refresh != m_editing_mode; - m_imgui->text(""); - - + + if (force_refresh) { // mode has just changed! + if (m_editing_mode) + m_editing_mode_cache = m_model_object->sla_support_points; + else + m_model_object->sla_support_points = m_editing_mode_cache; + } + */ + + bool force_refresh = false; + bool remove_all_points = false; + bool old_editing_state = m_editing_mode; if (m_editing_mode) { m_imgui->text(_(L("Left mouse click - add point"))); m_imgui->text(_(L("Right mouse click - remove point"))); - m_imgui->text(" "); - - + m_imgui->text(" "); // vertical gap + std::vector options = {"0.2", "0.4", "0.6", "0.8", "1.0"}; std::stringstream ss; ss << std::setprecision(1) << m_new_point_head_diameter; @@ -2130,30 +2142,77 @@ RENDER_AGAIN: bool changed = m_lock_unique_islands; m_imgui->checkbox(_(L("Lock supports under new islands")), m_lock_unique_islands); - force_refresh |= changed != m_lock_unique_islands; + force_refresh |= changed != m_lock_unique_islands; + + remove_all_points = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_editing_mode_cache.size())+")")); + + m_imgui->text(" "); // vertical gap + + bool apply_changes = m_imgui->button(_(L("Apply changes"))); + if (apply_changes) { + m_model_object->sla_support_points = m_editing_mode_cache; + m_editing_mode = false; + force_refresh = true; + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } + ImGui::SameLine(); + bool discard_changes = m_imgui->button(_(L("Cancel"))); + if (discard_changes) { + m_editing_mode_cache = m_model_object->sla_support_points; + m_editing_mode = false; + + m_grabbers.clear(); + for (const sla::SupportPoint& point : m_editing_mode_cache) { + m_grabbers.push_back(Grabber()); + m_grabbers.back().center = point.pos.cast(); + } + + force_refresh = true; + } } else { + //m_imgui->text("Some settings could"); + //m_imgui->text("be exposed here..."); + m_imgui->text(""); + m_imgui->text(""); + + m_imgui->text(""); + bool generate =m_imgui->button(_(L("Auto-generate points"))); force_refresh |= generate; - if (generate) + if (generate) { + m_model_object->sla_support_points.clear(); + m_grabbers.clear(); + m_editing_mode_cache.clear(); wxGetApp().plater()->reslice(); - } - - bool remove_all_clicked = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_model_object->sla_support_points.size())+")")); - - m_imgui->end(); - - if (remove_all_clicked) { - delete_current_grabber(true); - if (first_run) { - first_run = false; - goto RENDER_AGAIN; + } + ImGui::SameLine(); + bool editing_clicked = m_imgui->button("Editing"); + if (editing_clicked) { + m_editing_mode_cache = m_model_object->sla_support_points; + m_editing_mode = true; } } - if (remove_all_clicked || force_refresh) { + m_imgui->end(); + + if (m_editing_mode != old_editing_state) { // user just toggled between editing/non-editing mode + m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode); + force_refresh = true; + } + + + if (remove_all_points) { + force_refresh = true; + delete_current_grabber(true); + /*if (first_run) { + first_run = false; + goto RENDER_AGAIN; + }*/ + } + + if (force_refresh) { m_parent.reload_scene(true); - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } } #endif // ENABLE_IMGUI @@ -2174,6 +2233,20 @@ std::string GLGizmoSlaSupports::on_get_name() const return L("SLA Support Points [L]"); } +void GLGizmoSlaSupports::on_set_state() +{ + if (m_state == On) { + if (is_mesh_update_necessary()) + update_mesh(); + + m_parent.toggle_model_objects_visibility(false); + if (m_model_object) + m_parent.toggle_model_objects_visibility(true, m_model_object); + } + if (m_state == Off) + m_parent.toggle_model_objects_visibility(true); +} + // GLGizmoCut diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 7e2a3bb0b..06ef4b0d2 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -485,13 +485,10 @@ private: bool m_lock_unique_islands = false; bool m_editing_mode = false; float m_new_point_head_diameter = 0.4f; + std::vector m_editing_mode_cache; protected: - void on_set_state() override { - if (m_state == On && is_mesh_update_necessary()) { - update_mesh(); - } - } + void on_set_state() override; #if ENABLE_IMGUI virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) override;