From 40d52994db528d24ddc8d283c51b619347d3d642 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 17 Sep 2024 17:58:41 +0200 Subject: [PATCH] Distance of support point are modified by svg file. - Support points are stored in separate list(no need to collect from grid + no duplicit support points) - Grid contain only indices into result vector of support points - Each layer is made update of radiuses for existing support points - Each support point knows support radius for current processed layer - Layers knows z coordinate soo heights in preparation are copied into layer. --- resources/data/sla_support.svg | 361 +++++++++++++++++++ src/libslic3r/SLA/SupportPointGenerator.cpp | 245 +++++++++---- src/libslic3r/SLA/SupportPointGenerator.hpp | 26 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 70 ++-- 4 files changed, 586 insertions(+), 116 deletions(-) create mode 100644 resources/data/sla_support.svg diff --git a/resources/data/sla_support.svg b/resources/data/sla_support.svg new file mode 100644 index 0000000000..d2f4e02c38 --- /dev/null +++ b/resources/data/sla_support.svg @@ -0,0 +1,361 @@ + + + + + + + + + + + + + + + + 201. - 250. layer + + + + + + + + + + island supports + For loading is used only first red curveUse only line segments (no curve)Y coordinate of points must only increase.First point.y must be zero + Supported radius[in mm] + + Headinterface + + + + z [in mm]Print Direction + + + Last curve pointdefine stability requirements + 6. - 10. layer + Guid for layers height 0.05mm + 16. - 20. layer + 26. - 30. layer + 36. - 40. layer + 46. - 50. layer + 101. - 150. layer + + + diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 93b06ed604..e4e34402f7 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -22,21 +22,24 @@ class Grid2D coord_t m_cell_size; // Squar: x and y are same coord_t m_cell_size_half; - using Key = Point; + using Key = Point; // scaled point + using Value = size_t; // index into m_supports_ptr struct GridHash{std::size_t operator()(const Key &cell_id) const { return std::hash()(cell_id.x()) ^ std::hash()(cell_id.y() * 593); }}; - using Grid = std::unordered_multimap; + using Grid = std::unordered_multimap; Grid m_grid; - + // multiple grids points into same data storage of support points + LayerSupportPoints *m_supports_ptr; public: /// /// Set cell size for grid /// /// Granularity of stored points /// Must be bigger than maximal used radius - explicit Grid2D(const coord_t &cell_size) - : m_cell_size(cell_size), m_cell_size_half(cell_size / 2) {} + /// Pointer on Support vector + explicit Grid2D(const coord_t &cell_size, LayerSupportPoints* supports_ptr) + : m_cell_size(cell_size), m_cell_size_half(cell_size / 2), m_supports_ptr(supports_ptr) {} Key cell_id(const Point &point) const { Key::coord_type x = point.x() / m_cell_size; @@ -48,11 +51,19 @@ public: } void add(LayerSupportPoint &&point) { - m_grid.emplace(cell_id(point.position_on_layer), std::move(point)); + size_t index = m_supports_ptr->size(); + m_supports_ptr->emplace_back(std::move(point)); + m_grid.emplace(cell_id(point.position_on_layer), index); } using CheckFnc = std::function; bool exist_true_in_4cell_neighbor(const Point &pos, const CheckFnc& fnc) const { + // TODO: remove - test all support points without grid speed up + for (const auto &[key, value]: m_grid) + if(fnc(m_supports_ptr->at(value), pos)) + return true; + return false; + Key key = cell_id(pos); if (exist_true_for_cell(key, pos, fnc)) return true; Point un_cell_pos( @@ -72,19 +83,11 @@ public: assert(m_cell_size == grid.m_cell_size); m_grid.merge(std::move(grid.m_grid)); } - - LayerSupportPoints get_points() const { - LayerSupportPoints result; - result.reserve(m_grid.size()); - for (const auto& [key, support] : m_grid) - result.push_back(support); - return result; - } private: bool exist_true_for_cell(const Key &key, const Point &pos, const CheckFnc& fnc) const{ auto [begin_it, end_it] = m_grid.equal_range(key); for (Grid::const_iterator it = begin_it; it != end_it; ++it) { - const LayerSupportPoint &support_point = it->second; + const LayerSupportPoint &support_point = m_supports_ptr->at(it->second); if (fnc(support_point, pos)) return true; } @@ -135,34 +138,25 @@ Point intersection(const Point &p1, const Point &p2, const Point &cnt, double r2 return {}; } -coord_t get_supported_radius(const LayerSupportPoint &p, float z_distance, const SupportPointGeneratorConfig &config -) { - // TODO: calculate support radius - return scale_(5.); -} - -void sample_part( +/// +/// Move grid from previous layer to current one +/// for given part +/// +/// Grids generated in previous layer +/// Current layer part to process +/// Grids which will be moved to current grid +/// Grid for given part +Grid2D create_part_grid( + const LayerParts &prev_layer_parts, const LayerPart &part, - size_t layer_id, - const SupportPointGeneratorData &data, - const SupportPointGeneratorConfig &config, - std::vector &grids, std::vector &prev_grids ) { - // NOTE: first layer do not have prev part - assert(layer_id != 0); - - const Layers &layers = data.layers; - const LayerParts &prev_layer_parts = layers[layer_id - 1].parts; const LayerParts::const_iterator &prev_part_it = part.prev_parts.front().part_it; size_t index_of_prev_part = prev_part_it - prev_layer_parts.begin(); - if (prev_part_it->next_parts.size() == 1) { - grids.push_back(std::move(prev_grids[index_of_prev_part])); - } else { // Need a copy there are multiple parts above previus one - grids.push_back(prev_grids[index_of_prev_part]); // copy - } - // current part grid - Grid2D &part_grid = grids.back(); + Grid2D part_grid = (prev_part_it->next_parts.size() == 1)? + std::move(prev_grids[index_of_prev_part]) : + // Need a copy there are multiple parts above previus one + prev_grids[index_of_prev_part]; // copy // merge other grid in case of multiple previous parts for (size_t i = 1; i < part.prev_parts.size(); ++i) { @@ -175,16 +169,32 @@ void sample_part( part_grid.merge(std::move(grid_)); } } + return part_grid; +} - float part_z = data.heights[layer_id]; - Grid2D::CheckFnc is_supported = [part_z, &config] +/// +/// Add support point to part_grid when it is neccessary +/// +/// Current part - keep samples +/// Configuration to sample +/// Keep previous sampled suppport points +/// current z coordinate of part +void support_part_overhangs( + const LayerPart &part, + const SupportPointGeneratorConfig &config, + Grid2D &part_grid, + float part_z +) { + Grid2D::CheckFnc is_supported = [] (const LayerSupportPoint &support_point, const Point &p) -> bool { - float diff_height = part_z - support_point.pos.z(); - coord_t r_ = get_supported_radius(support_point, diff_height, config); + // Debug visualization of all sampled outline + //return false; + + coord_t r = support_point.current_radius; Point dp = support_point.position_on_layer - p; - if (std::abs(dp.x()) > r_) return false; - if (std::abs(dp.y()) > r_) return false; - double r2 = static_cast(r_); + if (std::abs(dp.x()) > r) return false; + if (std::abs(dp.y()) > r) return false; + double r2 = static_cast(r); r2 *= r2; return dp.cast().squaredNorm() < r2; }; @@ -201,7 +211,9 @@ void sample_part( SupportPointType::slope }, /* position_on_layer */ p, - /* direction_to_mass */ Point(1,0) + /* direction_to_mass */ Point(1,0), // TODO: change direction + /* radius_curve_index */ 0, + /* current_radius */ static_cast(scale_(config.support_curve.front().x())) }); } } @@ -212,14 +224,16 @@ Points uniformly_sample(const ExPolygon &island, const SupportPointGeneratorConf return Points{island.contour.centroid()}; } -Grid2D support_island(const LayerPart &part, float part_z, const SupportPointGeneratorConfig &cfg) { - // Maximal radius of supported area of one support point - double max_support_radius = 10.; // cfg.cell_size; - - // maximal radius of support - coord_t cell_size = scale_(max_support_radius); - - Grid2D part_grid(cell_size); +/// +/// Sample part as Island +/// Result store to grid +/// +/// Island to support +/// OUT place to store new supports +/// z coordinate of part +/// +void support_island(const LayerPart &part, Grid2D& part_grid, float part_z, + const SupportPointGeneratorConfig &cfg) { Points pts = uniformly_sample(*part.shape, cfg); for (const Point &pt : pts) part_grid.add(LayerSupportPoint{ @@ -229,9 +243,10 @@ Grid2D support_island(const LayerPart &part, float part_z, const SupportPointGen SupportPointType::island }, /* position_on_layer */ pt, - /* direction_to_mass */ Point(0,0) // direction from bottom + /* direction_to_mass */ Point(0,0), // direction from bottom + /* radius_curve_index */ 0, + /* current_radius */ static_cast(scale_(cfg.support_curve.front().x())) }); - return part_grid; } /// @@ -274,18 +289,19 @@ Slic3r::Points sample(Points::const_iterator b, Points::const_iterator e, double Slic3r::Points r; r.push_back(*b); - Points::const_iterator prev_pt = e; + //Points::const_iterator prev_pt = e; + const Point *prev_pt = nullptr; for (Points::const_iterator it = b; it+1 < e; ++it){ const Point &pt = *(it+1); double p_dist2 = (r.back() - pt).cast().squaredNorm(); while (p_dist2 > dist2) { // line segment goes out of radius - if (prev_pt == e) - prev_pt = it; + if (prev_pt == nullptr) + prev_pt = &(*it); r.push_back(intersection(*prev_pt, pt, r.back(), dist2)); p_dist2 = (r.back() - pt).cast().squaredNorm(); - prev_pt = r.end()-1; // r.back() + prev_pt = &r.back(); } - prev_pt = e; + prev_pt = nullptr; } return r; } @@ -382,13 +398,43 @@ Points sample_overhangs(const LayerPart& part, double dist2) { return samples; } + +void prepare_supports_for_layer(LayerSupportPoints &supports, float layer_z, + const SupportPointGeneratorConfig &config) { + const std::vector& curve = config.support_curve; + // calculate support area for each support point as radius + // IMPROVE: use some offsets of previous supported island + for (LayerSupportPoint &support : supports) { + size_t &index = support.radius_curve_index; + if (index + 1 >= curve.size()) + continue; // already contain maximal radius + + // find current segment + float diff_z = layer_z - support.pos.z(); + while ((index + 1) < curve.size() && diff_z > curve[index + 1].y()) + ++index; + + if ((index+1) >= curve.size()) { + // set maximal radius + support.current_radius = static_cast(scale_(curve.back().x())); + continue; + } + // interpolate radius on input curve + Vec2f a = curve[index]; + Vec2f b = curve[index+1]; + assert(a.y() <= diff_z && diff_z <= b.y()); + float t = (diff_z - a.y()) / (b.y() - a.y()); + assert(0 <= t && t <= 1); + support.current_radius = static_cast(scale_(a.x() + t * (b.x() - a.x()))); + } +} } // namespace #include "libslic3r/Execution/ExecutionSeq.hpp" SupportPointGeneratorData Slic3r::sla::prepare_generator_data( std::vector &&slices, - std::vector &&heights, + const std::vector &heights, ThrowOnCancel throw_on_cancel, StatusFunction statusfn ) { @@ -401,20 +447,20 @@ SupportPointGeneratorData Slic3r::sla::prepare_generator_data( // Move input into result SupportPointGeneratorData result; result.slices = std::move(slices); - result.heights = std::move(heights); // Allocate empty layers. result.layers = Layers(result.slices.size(), {}); // Generate Extents and SampleLayers execution::for_each(ex_tbb, size_t(0), result.slices.size(), - [&result, throw_on_cancel](size_t layer_id) { + [&result, &heights, throw_on_cancel](size_t layer_id) { if ((layer_id % 8) == 0) // Don't call the following function too often as it flushes // CPU write caches due to synchronization primitves. throw_on_cancel(); Layer &layer = result.layers[layer_id]; + layer.print_z = heights[layer_id]; // copy const ExPolygons &islands = result.slices[layer_id]; layer.parts.reserve(islands.size()); for (const ExPolygon &island : islands) { @@ -467,6 +513,42 @@ SupportPointGeneratorData Slic3r::sla::prepare_generator_data( return result; } +#include "libslic3r/NSVGUtils.hpp" +#include "libslic3r/Utils.hpp" +std::vector load_curve_from_file() { + std::string filePath = Slic3r::resources_dir() + "/data/sla_support.svg"; + EmbossShape::SvgFile svg_file{filePath}; + NSVGimage *image = init_image(svg_file); + + for (NSVGshape *shape_ptr = image->shapes; shape_ptr != NULL; shape_ptr = shape_ptr->next) { + const NSVGshape &shape = *shape_ptr; + if (!(shape.flags & NSVG_FLAGS_VISIBLE)) continue; // is visible + if (shape.fill.type != NSVG_PAINT_NONE) continue; // is not used fill + if (shape.stroke.type == NSVG_PAINT_NONE) continue; // exist stroke + if (shape.strokeWidth < 1e-5f) continue; // is visible stroke width + if (shape.stroke.color != 4278190261) continue; // is red + + // use only first path + const NSVGpath *path = shape.paths; + size_t count_points = path->npts; + assert(count_points > 1); + --count_points; + std::vector points; + points.reserve(count_points/3+1); + points.push_back({path->pts[0], path->pts[1]}); + for (size_t i = 0; i < count_points; i += 3) { + const float *p = &path->pts[i * 2]; + points.push_back({p[6], p[7]}); + } + assert(points.size() >= 2); + return points; + } + + // red curve line is not found + assert(false); + return {}; +} + LayerSupportPoints Slic3r::sla::generate_support_points( const SupportPointGeneratorData &data, const SupportPointGeneratorConfig &config, @@ -478,30 +560,41 @@ LayerSupportPoints Slic3r::sla::generate_support_points( double status = 0; // current progress int status_int = 0; + // Hack to set curve for testing + if (config.support_curve.empty()) + const_cast(config).support_curve = load_curve_from_file(); + + // Maximal radius of supported area of one support point + double max_support_radius = config.support_curve.back().x(); // cfg.cell_size; + // maximal radius of support + coord_t cell_size = scale_(2*max_support_radius); + + // Storage for support points used by grid LayerSupportPoints result; + + // grid index == part in layer index std::vector prev_grids; // same count as previous layer item size for (size_t layer_id = 0; layer_id < layers.size(); ++layer_id) { const Layer &layer = layers[layer_id]; + prepare_supports_for_layer(result, layer.print_z, config); + + // grid index == part in layer index std::vector grids; grids.reserve(layer.parts.size()); for (const LayerPart &part : layer.parts) { if (part.prev_parts.empty()) { + // only island add new grid + grids.emplace_back(cell_size, &result); // new island - needs support no doubt - float part_z = data.heights[layer_id]; - grids.push_back(support_island(part, part_z, config)); + support_island(part, grids.back(), layer.print_z, config); } else { - sample_part(part, layer_id, data, config, grids, prev_grids); - } - - // collect result from grid of top part - if (part.next_parts.empty()) { - const Grid2D &part_grid = grids.back(); - LayerSupportPoints sps = part_grid.get_points(); - result.insert(result.end(), - std::make_move_iterator(sps.begin()), - std::make_move_iterator(sps.end())); + // first layer should have empty prev_part + assert(layer_id != 0); + const LayerParts &prev_layer_parts = layers[layer_id - 1].parts; + grids.push_back(create_part_grid(prev_layer_parts, part, prev_grids)); + support_part_overhangs(part, config, grids.back(), layer.print_z); } } prev_grids = std::move(grids); diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index f6c571cdd0..b8976d166c 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -40,6 +40,12 @@ struct SupportPointGeneratorConfig{ // Minimal island Area to print - TODO: Should be modifiable from UI // !! Filter should be out of sampling algorithm !! float minimal_island_area = pow(0.047f, 2.f); // [in mm^2] pixel_area + + // maximal distance to nearest support point(define radiuses per layer) + // x axis .. mean distance on layer(XY) + // y axis .. mean difference of height(Z) + // Points of lines [in mm] + std::vector support_curve; }; struct LayerPart; // forward decl. @@ -93,6 +99,15 @@ struct LayerSupportPoint: public SupportPoint // used as ray to positioning 3d point on mesh surface // Island has direction [0,0] - should be placed on surface from bottom Point direction_to_mass; + + // index into curve to faster found radius for current layer + size_t radius_curve_index = 0; + coord_t current_radius = 0; // [in scaled mm] + + // information whether support point is active in current investigated layer + // Flagged false when no part on layer in Radius 'r' around support point + // Tool to support overlapped overhang area multiple times + bool active_in_part = true; }; using LayerSupportPoints = std::vector; @@ -106,8 +121,7 @@ struct Layer size_t layer_id; // Absolute distance from Zero - copy value from heights - // [[deprecated]] Use index to layers insted of adress from item - double print_z; // [in mm] + float print_z; // [in mm] // data for one expolygon LayerParts parts; @@ -122,10 +136,10 @@ struct SupportPointGeneratorData { // Input slices of mesh std::vector slices; - // Same size as slices - std::vector heights; - // link to slices + // Keep information about layer and its height + // and connection between layers for its part + // NOTE: contain links into slices Layers layers; }; @@ -147,7 +161,7 @@ using StatusFunction= std::function; /// Data prepared for generate support points SupportPointGeneratorData prepare_generator_data( std::vector &&slices, - std::vector &&heights, + const std::vector &heights, ThrowOnCancel throw_on_cancel, StatusFunction statusfn ); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index e2b8946fc9..ebcbe1747c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -678,45 +678,47 @@ RENDER_AGAIN: else { // not in editing mode: m_imgui->disabled_begin(!is_input_enabled()); - ImGui::AlignTextToFramePadding(); - ImGuiPureWrap::text(m_desc.at("minimal_distance")); - ImGui::SameLine(settings_sliders_left); - ImGui::PushItemWidth(window_width - settings_sliders_left); + ImGui::Text("Distribution depends on './resources/data/sla_support.svg'\ninstruction for edit are in file"); - std::vector opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"}); - float density = static_cast(opts[0])->value; - float minimal_point_distance = static_cast(opts[1])->value; + //ImGui::AlignTextToFramePadding(); + //ImGuiPureWrap::text(m_desc.at("minimal_distance")); + //ImGui::SameLine(settings_sliders_left); + //ImGui::PushItemWidth(window_width - settings_sliders_left); - m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm"); - bool slider_clicked = m_imgui->get_last_slider_status().clicked; // someone clicked the slider - bool slider_edited = m_imgui->get_last_slider_status().edited; // someone is dragging the slider - bool slider_released = m_imgui->get_last_slider_status().deactivated_after_edit; // someone has just released the slider + //std::vector opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"}); + //float density = static_cast(opts[0])->value; + //float minimal_point_distance = static_cast(opts[1])->value; - ImGui::AlignTextToFramePadding(); - ImGuiPureWrap::text(m_desc.at("points_density")); - ImGui::SameLine(settings_sliders_left); + //m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm"); + //bool slider_clicked = m_imgui->get_last_slider_status().clicked; // someone clicked the slider + //bool slider_edited = m_imgui->get_last_slider_status().edited; // someone is dragging the slider + //bool slider_released = m_imgui->get_last_slider_status().deactivated_after_edit; // someone has just released the slider - m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%"); - slider_clicked |= m_imgui->get_last_slider_status().clicked; - slider_edited |= m_imgui->get_last_slider_status().edited; - slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; + //ImGui::AlignTextToFramePadding(); + //ImGuiPureWrap::text(m_desc.at("points_density")); + //ImGui::SameLine(settings_sliders_left); - if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo - m_minimal_point_distance_stash = minimal_point_distance; - m_density_stash = density; - } - if (slider_edited) { - mo->config.set("support_points_minimal_distance", minimal_point_distance); - mo->config.set("support_points_density_relative", (int)density); - } - if (slider_released) { - mo->config.set("support_points_minimal_distance", m_minimal_point_distance_stash); - mo->config.set("support_points_density_relative", (int)m_density_stash); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support parameter change")); - mo->config.set("support_points_minimal_distance", minimal_point_distance); - mo->config.set("support_points_density_relative", (int)density); - wxGetApp().obj_list()->update_and_show_object_settings_item(); - } + //m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%"); + //slider_clicked |= m_imgui->get_last_slider_status().clicked; + //slider_edited |= m_imgui->get_last_slider_status().edited; + //slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; + + //if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo + // m_minimal_point_distance_stash = minimal_point_distance; + // m_density_stash = density; + //} + //if (slider_edited) { + // mo->config.set("support_points_minimal_distance", minimal_point_distance); + // mo->config.set("support_points_density_relative", (int)density); + //} + //if (slider_released) { + // mo->config.set("support_points_minimal_distance", m_minimal_point_distance_stash); + // mo->config.set("support_points_density_relative", (int)m_density_stash); + // Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support parameter change")); + // mo->config.set("support_points_minimal_distance", minimal_point_distance); + // mo->config.set("support_points_density_relative", (int)density); + // wxGetApp().obj_list()->update_and_show_object_settings_item(); + //} bool generate = ImGuiPureWrap::button(m_desc.at("auto_generate"));