From 8b4bd7177bea648942edb84044bc7b17beeba357 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 19 Feb 2019 10:09:41 +0100 Subject: [PATCH] SLA auto supports: Work with support force deficit to sprinkle support points. Use OpenGL emissive material to render support points. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 108 +++++++++++++++++--------- src/libslic3r/SLA/SLAAutoSupports.hpp | 20 ++++- src/slic3r/GUI/GLGizmo.cpp | 8 ++ src/slic3r/GUI/SysInfoDialog.cpp | 2 + 4 files changed, 98 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 1d52dff6f..d1d122835 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -193,49 +193,54 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: point_grid.cell_size = Vec3f(10.f, 10.f, 10.f); for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) { - SLAAutoSupports::MyLayer &layer_top = layers[layer_id]; - for (Structure &top : layer_top.islands) + SLAAutoSupports::MyLayer *layer_top = &layers[layer_id]; + SLAAutoSupports::MyLayer *layer_bottom = (layer_id > 0) ? &layers[layer_id - 1] : nullptr; + std::vector support_force_bottom; + if (layer_bottom != nullptr) { + support_force_bottom.assign(layer_bottom->islands.size(), 0.f); + for (size_t i = 0; i < layer_bottom->islands.size(); ++ i) + support_force_bottom[i] = layer_bottom->islands[i].supports_force_total(); + } + for (Structure &top : layer_top->islands) for (Structure *bottom : top.islands_below) { float centroids_dist = (bottom->centroid - top.centroid).norm(); // Penalization resulting from centroid offset: // bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); - bottom->supports_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom->area)); + float &support_force = support_force_bottom[bottom - layer_bottom->islands.data()]; +//FIXME this condition does not reflect a bifurcation into a one large island and one tiny island well, it incorrectly resets the support force to zero. +// One should rather work with the overlap area vs overhang area. +// support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom->area)); // Penalization resulting from increasing polygon area: - bottom->supports_force *= std::min(1.f, 20.f * bottom->area / top.area); + support_force *= std::min(1.f, 20.f * bottom->area / top.area); } // Let's assign proper support force to each of them: if (layer_id > 0) { - for (Structure &below : layers[layer_id - 1].islands) { + for (Structure &below : layer_bottom->islands) { + float below_support_force = support_force_bottom[&below - layer_bottom->islands.data()]; float above_area = 0.f; for (Structure *above : below.islands_above) above_area += above->area; for (Structure *above : below.islands_above) - above->supports_force += below.supports_force * above->area / above_area; + above->supports_force_inherited += below_support_force * above->area / above_area; } } // Now iterate over all polygons and append new points if needed. - for (Structure &s : layer_top.islands) { + for (Structure &s : layer_top->islands) { + // Penalization resulting from large diff from the last layer: +// s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); + s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); + + float force_deficit = s.support_force_deficit(m_config.tear_pressure); if (s.islands_below.empty()) // completely new island - needs support no doubt - uniformly_cover(*s.polygon, s, point_grid, true); - else + uniformly_cover({ *s.polygon }, s, point_grid, true); + else if (! s.dangling_areas.empty()) { // Let's see if there's anything that overlaps enough to need supports: // What we now have in polygons needs support, regardless of what the forces are, so we can add them. - for (const ExPolygon& p : s.dangling_areas) - //FIXME is it an island point or not? Vojtech thinks it is. - uniformly_cover(p, s, point_grid); - } - - // We should also check if current support is enough given the polygon area. - for (Structure& s : layer_top.islands) { - // Penalization resulting from large diff from the last layer: -// s.supports_force /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); - s.supports_force /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); - if (s.area * m_config.tear_pressure > s.supports_force) { - //FIXME Don't calculate area inside the compare function! - //FIXME Cover until the force deficit is covered. Cover multiple areas, sort by decreasing area. - if (! s.overhangs.empty()) - //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. - uniformly_cover(s.overhangs.front(), s, point_grid); + //FIXME is it an island point or not? Vojtech thinks it is. + uniformly_cover(s.dangling_areas, s, point_grid); + } else if (! s.overhangs.empty()) { + //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. + uniformly_cover(s.overhangs, s, point_grid); } } @@ -304,6 +309,14 @@ std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float return out; } +std::vector sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) +{ + std::vector out; + for (const ExPolygon &expoly : expolys) + append(out, sample_expolygon_with_boundary(expoly, samples_per_mm2, samples_per_mm_boundary, rng)); + return out; +} + template static inline std::vector poisson_disk_from_samples(const std::vector &raw_samples, float radius, REFUSE_FUNCTION refuse_function) { @@ -419,32 +432,51 @@ static inline std::vector poisson_disk_from_samples(const std::vector raw_samples = sample_expolygon_with_boundary(island, samples_per_mm2, 5.f / poisson_radius, rng); - std::vector poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, - [&structure, &grid3d, min_spacing](const Vec2f &pos) { - return grid3d.collides_with(pos, &structure, min_spacing); - }); + std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng); + std::vector poisson_samples; + for (size_t iter = 0; iter < 4; ++ iter) { + poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, + [&structure, &grid3d, min_spacing](const Vec2f &pos) { + return grid3d.collides_with(pos, &structure, min_spacing); + }); + if (poisson_samples.size() >= poisson_samples_target) + break; + float coeff = 0.5f; + if (poisson_samples.size() * 2 > poisson_samples_target) + coeff = float(poisson_samples.size()) / float(poisson_samples_target); + poisson_radius *= coeff; + min_spacing *= coeff; + } #ifdef SLA_AUTOSUPPORTS_DEBUG { static int irun = 0; Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(island)); - svg.draw(island); + for (const ExPolygon &island : islands) + svg.draw(island); for (const Vec2f &pt : raw_samples) svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red"); for (const Vec2f &pt : poisson_samples) @@ -453,9 +485,13 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct #endif /* NDEBUG */ // assert(! poisson_samples.empty()); + if (poisson_samples_target < poisson_samples.size()) { + std::shuffle(poisson_samples.begin(), poisson_samples.end(), rng); + poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end()); + } for (const Vec2f &pt : poisson_samples) { m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island); - structure.supports_force += m_config.support_force; + structure.supports_force_this_layer += m_config.support_force; grid3d.insert(pt, &structure); } } diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 979b75e4f..bad408d04 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -18,8 +18,9 @@ public: float density_at_45; float minimal_z; /////////////// - float support_force = 30.f; // a force one point can support (arbitrary force unit) - float tear_pressure = 1.f; // pressure that the display exerts (the force unit per mm2) +// float support_force = 30.f; // a force one point can support (arbitrary force unit) + float support_force = 10.f; // a force one point can support (arbitrary force unit) + float tear_pressure = 1.f; // pressure that the display exerts (the force unit per mm2) }; SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, @@ -43,13 +44,22 @@ public: float height = 0; // How well is this ExPolygon held to the print base? // Positive number, the higher the better. - float supports_force = 0.f; + float supports_force_this_layer = 0.f; + float supports_force_inherited = 0.f; + float supports_force_total() const { return this->supports_force_this_layer + this->supports_force_inherited; } #ifdef SLA_AUTOSUPPORTS_DEBUG std::chrono::milliseconds unique_id; #endif /* SLA_AUTOSUPPORTS_DEBUG */ +#ifdef NDEBUG + // In release mode, use the optimized container. boost::container::small_vector islands_above; boost::container::small_vector islands_below; +#else + // In debug mode, use the standard vector, which is well handled by debugger visualizer. + std::vector islands_above; + std::vector islands_below; +#endif ExPolygons dangling_areas; ExPolygons overhangs; float overhangs_area; @@ -80,6 +90,8 @@ public: out.emplace_back(*below->polygon); return out; } + // Positive deficit of the supports. If negative, this area is well supported. If positive, more supports need to be added. + float support_force_deficit(const float tear_pressure) const { return this->area * tear_pressure - this->supports_force_total(); } }; struct MyLayer { @@ -155,7 +167,7 @@ private: float m_supports_force_total = 0.f; void process(const std::vector& slices, const std::vector& heights); - void uniformly_cover(const ExPolygon& island, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); + void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); void project_onto_mesh(std::vector& points) const; #ifdef SLA_AUTOSUPPORTS_DEBUG diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 73a65aedf..259fc4ca7 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1919,6 +1919,8 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b } } ::glColor3fv(render_color); + float render_color_emissive[4] = { 0.5 * render_color[0], 0.5 * render_color[1], 0.5 * render_color[2], 1.f}; + ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive); // Now render the sphere. Inverse matrix of the instance scaling is applied so that the // sphere does not scale with the object. @@ -1929,6 +1931,12 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b ::glPopMatrix(); } + { + // Reset emissive component to zero (the default value) + float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f }; + ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive); + } + if (!picking) ::glDisable(GL_LIGHTING); diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index fd8ab5f93..8f7c65d7e 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -3,6 +3,8 @@ #include "3DScene.hpp" #include "GUI.hpp" +#include + #include #include