diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp index 433c32b8a3..ce502a08b1 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp @@ -16,7 +16,6 @@ struct SampleConfig // Every point on island has at least one support point in maximum distance // MUST be bigger than zero coord_t max_distance = static_cast(scale_(5.)); - coord_t half_distance = static_cast(scale_(2.5)); // has to be half od max_distance // Support point head radius // MUST be bigger than zero @@ -31,25 +30,18 @@ struct SampleConfig // Must be bigger than minimal_distance_from_outline coord_t maximal_distance_from_outline = static_cast(scale_(1.));// [nano meter] - // When angle on outline is smaller than max_interesting_angle - // than create unmovable support point. - // Should be in range from Pi/2 to Pi - double max_interesting_angle = 2. / 3. * M_PI; // [radians] - - // Distinguish when to add support point on VD outline point(center line sample) - // MUST be bigger than minimal_distance_from_outline - coord_t minimal_support_distance = 0; - - // minimal length of side branch to be sampled - // it is used for sampling in center only - coord_t min_side_branch_length = 0; - // Maximal length of longest path in voronoi diagram to be island // supported only by one single support point this point will be in center of path. coord_t max_length_for_one_support_point = static_cast(scale_(1.)); // Maximal length of island supported by 2 points coord_t max_length_for_two_support_points = static_cast(scale_(1.)); + // Maximal ratio of path length for island supported by 2 points + // Used only in case when maximal_distance_from_outline is bigger than + // current island longest_path * this ratio + // Note: Preven for tiny island to contain overlapped support points + // must be smaller than 0.5 and bigger than zero + float max_length_ratio_for_two_support_points = 0.25; // |--25%--Sup----50%----Sup--25%--| // Maximal width of line island supported in the middle of line // Must be greater or equal to min_width_for_outline_support @@ -71,7 +63,7 @@ struct SampleConfig // Sample outline of Field by this value // Less than max_distance - coord_t outline_sample_distance = 2; + coord_t outline_sample_distance = static_cast(scale_(5 *3/4.)); // Maximal distance over Voronoi diagram edges to find closest point during aligning Support point coord_t max_align_distance = 0; // [scaled mm -> nanometers] diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.cpp b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.cpp index e709eea707..a669b76c20 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.cpp @@ -62,15 +62,10 @@ SampleConfig SampleConfigFactory::create(float support_head_diameter_in_mm) // TODO: find valid params !!!! SampleConfig result; result.max_distance = max_distance; - result.half_distance = result.max_distance / 2; result.head_radius = head_diameter / 2; result.minimal_distance_from_outline = result.head_radius; result.maximal_distance_from_outline = result.max_distance/3; assert(result.minimal_distance_from_outline < result.maximal_distance_from_outline); - result.minimal_support_distance = result.minimal_distance_from_outline + - result.half_distance; - - result.min_side_branch_length = 2 * result.minimal_distance_from_outline + result.max_distance/4; result.max_length_for_one_support_point = max_distance / 3 + 2 * result.minimal_distance_from_outline + diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp index 20f58e6502..da58594357 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp @@ -10,28 +10,12 @@ SupportIslandPoint::SupportIslandPoint(Slic3r::Point point, Type type) bool SupportIslandPoint::can_move(const Type &type) { - // use shorter list - /* - static const std::set can_move({ - Type::center_line, - Type::center_circle, - Type::center_circle_end, - Type::center_circle_end2, - Type::outline, - Type::inner - }); - return can_move.find(type) != can_move.end(); - /*/ // switch comment center static const std::set cant_move({ + Type::one_bb_center_point, Type::one_center_point, Type::two_points, - Type::center_line_end, - Type::center_line_end2, - Type::center_line_end3, - Type::center_line_start }); return cant_move.find(type) == cant_move.end(); - //*/ } bool SupportIslandPoint::can_move() const { return can_move(type); } @@ -45,26 +29,20 @@ coord_t SupportIslandPoint::move(const Point &destination) std::string SupportIslandPoint::to_string(const Type &type) { - static const std::string undefined = "UNDEFINED"; static std::map type_to_string= - {{Type::one_center_point, "one_center_point"}, - {Type::two_points,"two_points"}, - {Type::center_line, "center_line"}, - {Type::center_line1, "center_line1"}, - {Type::center_line2, "center_line2"}, - {Type::center_line3, "center_line3"}, - {Type::center_line_end, "center_line_end"}, - {Type::center_line_end2, "center_line_end2"}, - {Type::center_line_end3, "center_line_end3"}, - {Type::center_line_start, "center_line_start"}, - {Type::center_circle, "center_circle"}, - {Type::center_circle_end, "center_circle_end"}, - {Type::center_circle_end2, "center_circle_end2"}, - {Type::outline, "outline"}, - {Type::inner, "inner"}, - {Type::undefined, "undefined"}}; + {{Type::one_center_point, "one_center_point"}, + {Type::two_points, "two_points"}, + {Type::two_points_backup, "two_points_backup"}, + {Type::one_bb_center_point,"one_bb_center_point"}, + {Type::thin_part, "thin_part"}, + {Type::thin_part_change, "thin_part_change"}, + {Type::thin_part_loop, "thin_part_loop"}, + {Type::thick_part_outline, "thick_part_outline"}, + {Type::thick_part_inner, "thick_part_inner"}, + {Type::undefined, "undefined"}}; auto it = type_to_string.find(type); - if (it == type_to_string.end()) return undefined; + if (it == type_to_string.end()) + return to_string(Type::undefined); return it->second; } diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp index 56ea11e244..587bdc9244 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp @@ -16,25 +16,15 @@ class SupportIslandPoint { public: enum class Type: unsigned char { - one_center_point, - two_points, - center_line, - center_line1, // sample line in center - center_line2, // rest of neighbor edge before position of Field start - center_line3, // end of loop, next neighbors are already sampled - center_line_end, // end of branch - center_line_end2, // start of main path(only one per VD) - center_line_end3, // end in continous sampling - center_line_start, // first sample - center_circle, - center_circle_end, // circle finish by one point (one end per circle - - // need allign) - center_circle_end2, // circle finish by multi points (one end per - // circle - need allign) - outline, // keep position align with island outline - inner, // point inside wide part, without restriction on move - - one_bb_center_point, // for island smaller than head radius + one_bb_center_point, // for island smaller than head radius + one_center_point, // small enough to support only by one support point + two_points, // island stretched between two points + two_points_backup, // same as before but forced after divide to thin&thick with small amoutn of points + thin_part, // point on thin part of island lay on VD + thin_part_change, // on the first edge -> together with change to thick part of island + thin_part_loop, // on the last edge -> loop into itself part of island + thick_part_outline, // keep position align with island outline + thick_part_inner, // point inside wide part, without restriction on move undefined }; @@ -128,7 +118,7 @@ public: public: SupportCenterIslandPoint(VoronoiGraph::Position position, const SampleConfig *configuration, - Type type = Type::center_line); + Type type = Type::thin_part); bool can_move() const override{ return true; } coord_t move(const Point &destination) override; @@ -166,7 +156,7 @@ public: public: SupportOutlineIslandPoint(Position position, std::shared_ptr restriction, - Type type = Type::outline); + Type type = Type::thick_part_outline); // return true bool can_move() const override; @@ -217,7 +207,7 @@ public: { assert(lines.size() == lengths.size()); } - + virtual ~Restriction() = default; virtual std::optional next_index(size_t index) const = 0; virtual std::optional prev_index(size_t index) const = 0; }; @@ -299,7 +289,7 @@ class SupportIslandInnerPoint: public SupportIslandPoint public: SupportIslandInnerPoint(Point point, std::shared_ptr inner, - Type type = Type::inner); + Type type = Type::thick_part_inner); bool can_move() const override { return true; }; diff --git a/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp b/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp index f51571b4b6..80473c828a 100644 --- a/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp +++ b/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp @@ -1,9 +1,11 @@ -#include "SampleIslandUtils.hpp" +#include "UniformSupportIsland.hpp" #include #include #include #include +#include +#include #include // allign #include @@ -28,22 +30,19 @@ // comment definition of NDEBUG to enable assert() //#define NDEBUG -#define SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH "C:/data/temp/Field_<>.svg" +//#define SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH "C:/data/temp/Field_<>.svg" //#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG_PATH "C:/data/temp/align/island_<>_aligned.svg" //#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH "C:/data/temp/align_once/iter_<>.svg" //#define SLA_SAMPLE_ISLAND_UTILS_DEBUG_CELL_DISTANCE_PATH "C:/data/temp/island_cell.svg" -#include - +namespace { using namespace Slic3r; using namespace Slic3r::sla; -namespace { -// TODO: Move to string utils - /// /// Replace first occurence of string +/// TODO: Generalize and Move into string utils /// /// /// @@ -62,7 +61,6 @@ std::string replace_first( /// /// IMPROVE: use Slic3r::BoundingBox -/// /// Search for reference to an Expolygon with biggest contour /// /// Input @@ -140,7 +138,7 @@ ExPolygon get_simplified(const ExPolygon &island, const SampleConfig &config) { /// /// Transform support point to slicer points /// -Slic3r::Points to_points(const SupportIslandPoints &support_points){ +Points to_points(const SupportIslandPoints &support_points){ Points result; result.reserve(support_points.size()); std::transform(support_points.begin(), support_points.end(), std::back_inserter(result), @@ -178,95 +176,8 @@ SVG draw_island_graph(const std::string &path, const ExPolygon &island, /// Term criteria for align: Minimal sample move and Maximal count of iteration void align_samples(SupportIslandPoints &samples, const ExPolygon &island, const SampleConfig &config); -/// -/// Decide how to sample path -/// -/// Path inside voronoi diagram with side branches and circles -/// Source lines for VG --> outline of island. -/// Definition how to sample -/// Support points for island -static SupportIslandPoints sample_expath(const VoronoiGraph::ExPath &path, const Lines &lines, const SampleConfig &config); - void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radius, bool write_type = true); -} // namespace -SupportIslandPoints SampleIslandUtils::uniform_cover_island( - const ExPolygon &island, const SampleConfig &config -) { - ExPolygon simplified_island = get_simplified(island, config); -#ifdef OPTION_TO_STORE_ISLAND - std::string path; - if (!config.path.empty()) { - static int counter = 0; - path = replace_first(config.path, "<>", std::to_string(++counter)); - draw_island(path, island, simplified_island); - // need to save svg in case of infinite loop so no store SVG into variable - } -#endif // OPTION_TO_STORE_ISLAND - - // When island is smaller than minimal-head diameter, - // it will be supported whole by support poin in center - if (Point center; get_center(simplified_island.contour.points, config.head_radius, center)) { - SupportIslandPoints result; - result.push_back(std::make_unique( - center, SupportIslandInnerPoint::Type::one_bb_center_point)); -#ifdef OPTION_TO_STORE_ISLAND - if (!path.empty()){ // add center support point into image - SVG svg = draw_island(path, island, simplified_island); - svg.draw(center, "black", config.head_radius); - svg.draw_text(center, "Center of bounding box", "black"); - } - return result; -#endif // OPTION_TO_STORE_ISLAND - } - - Slic3r::Geometry::VoronoiDiagram vd; - Lines lines = to_lines(simplified_island); - vd.construct_voronoi(lines.begin(), lines.end()); - Slic3r::Voronoi::annotate_inside_outside(vd, lines); - VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines); - VoronoiGraph::ExPath longest_path; - - const VoronoiGraph::Node *start_node = VoronoiGraphUtils::getFirstContourNode(skeleton); - // every island has to have a point on contour - assert(start_node != nullptr); - longest_path = VoronoiGraphUtils::create_longest_path(start_node); - -#ifdef OPTION_TO_STORE_ISLAND // add voronoi diagram with longest path into image - if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config); -#endif // OPTION_TO_STORE_ISLAND - - SupportIslandPoints samples = sample_expath(longest_path, lines, config); - -#ifdef OPTION_TO_STORE_ISLAND - Points samples_before_align = ::to_points(samples); - if (!path.empty()) { - SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config); - draw(svg, samples, config.head_radius); - } -#endif // OPTION_TO_STORE_ISLAND - - // allign samples - align_samples(samples, island, config); -#ifdef OPTION_TO_STORE_ISLAND - if (!path.empty()) { - SVG svg = draw_island(path, island, simplified_island); - coord_t width = config.head_radius / 5; - VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "darkorange"); - VoronoiGraphUtils::draw(svg, skeleton, lines, config, false /*print Pointer address*/); - - Lines align_moves; - align_moves.reserve(samples.size()); - for (size_t i = 0; i < samples.size(); ++i) - align_moves.push_back(Line(samples[i]->point, samples_before_align[i])); - svg.draw(align_moves, "lightgray", width); - draw(svg, samples, config.head_radius); - } -#endif // OPTION_TO_STORE_ISLAND - return samples; -} - -namespace { /// /// Create unique static support point /// @@ -384,26 +295,20 @@ SupportIslandPointPtr create_middle_path_point( if (!position_opt.has_value()) return nullptr; return create_no_move_point(*position_opt, type); } -} // namespace #ifndef NDEBUG -namespace { -bool is_points_in_distance(const Slic3r::Point & p, - const Slic3r::Points &points, +bool is_points_in_distance(const Point & p, + const Points &points, double max_distance) { - for (const auto &p2 : points) { - double d = (p - p2).cast().norm(); - if (d > max_distance) - return false; - } - return true; + return std::all_of(points.begin(), points.end(), + [p, max_distance](const Point &point) { + double d = (p - point).cast().norm(); + return d <= max_distance; + }); } -} // namespace #endif // NDEBUG -// align -namespace { /// /// once align /// @@ -422,7 +327,7 @@ coord_t align_once( // https://stackoverflow.com/questions/23823345/how-to-construct-a-voronoi-diagram-inside-a-polygon // IMPROVE1: add accessor to point coordinate do not copy points // IMPROVE2: add filter for create cell polygon only for moveable samples - Slic3r::Points points = to_points(samples); + Points points = to_points(samples); assert(!has_duplicate_points(points)); Polygons cell_polygons = create_voronoi_cells_cgal(points, config.max_distance); @@ -463,7 +368,7 @@ coord_t align_once( continue; // do not align point with invalid cell // IMPROVE: add intersection polygon with expolygon - Polygons intersections = Slic3r::intersection(cell_polygon, island); + Polygons intersections = intersection(cell_polygon, island); const Polygon *island_cell = nullptr; if (intersections.size() == 1) { island_cell = &intersections.front(); @@ -559,12 +464,9 @@ void align_samples(SupportIslandPoints &samples, const ExPolygon &island, const } -} // namespace - /// /// Separation of thin and thick part of island /// -namespace { using VD = Slic3r::Geometry::VoronoiDiagram; using Position = VoronoiGraph::Position; @@ -610,8 +512,11 @@ using ThickParts = std::vector; /// One thin part of island /// [OUTPUT]Set of support points /// Define density of support points -void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &results, const SampleConfig &config) { - struct SupportIn { +void create_supports_for_thin_part( + const ThinPart &part, SupportIslandPoints &results, const SampleConfig &config +) { + struct SupportIn + { // want to create support in coord_t support_in; // [nano meters] // Neighbor to continue is not sampled yet @@ -620,13 +525,14 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re using SupportIns = std::vector; coord_t support_distance = config.max_distance; - coord_t half_support_distance = support_distance/2; - + coord_t half_support_distance = support_distance / 2; + // Current neighbor SupportIn curr{half_support_distance + part.center.calc_distance(), part.center.neighbor}; const Neighbor *twin_start = VoronoiGraphUtils::get_twin(*curr.neighbor); - coord_t twin_support_in = static_cast(twin_start->length()) - curr.support_in + support_distance; - + coord_t twin_support_in = static_cast(twin_start->length()) - curr.support_in + + support_distance; + // Process queue SupportIns process; process.push_back(SupportIn{twin_support_in, twin_start}); @@ -634,33 +540,32 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re // Loop over thin part of island to create support points on the voronoi skeleton. while (curr.neighbor != nullptr || !process.empty()) { if (curr.neighbor == nullptr) { // need to pop next one from process - curr = process.back(); // copy + curr = process.back(); // copy process.pop_back(); } - auto part_end_it = std::lower_bound(part.ends.begin(), part.ends.end(), curr.neighbor, - [](const Position &end, const Neighbor *n) { return end.neighbor < n; }); - bool is_end_neighbor = part_end_it != part.ends.end() && - curr.neighbor == part_end_it->neighbor; + auto part_end_it = std::lower_bound(part.ends.begin(), part.ends.end(), curr.neighbor, + [](const Position &end, const Neighbor *n) { return end.neighbor < n; }); + bool is_end_neighbor = part_end_it != part.ends.end() && + curr.neighbor == part_end_it->neighbor; // add support on current neighbor - coord_t edge_length = (is_end_neighbor) ? - part_end_it->calc_distance() : - static_cast(curr.neighbor->length()); + coord_t edge_length = (is_end_neighbor) ? part_end_it->calc_distance() : + static_cast(curr.neighbor->length()); while (edge_length >= curr.support_in) { double ratio = curr.support_in / curr.neighbor->length(); VoronoiGraph::Position position(curr.neighbor, ratio); results.push_back(std::make_unique( - position, &config, SupportIslandPoint::Type::center_line1)); + position, &config, SupportIslandPoint::Type::thin_part_change)); curr.support_in += support_distance; } - curr.support_in -= edge_length; + curr.support_in -= edge_length; - if (is_end_neighbor) { + if (is_end_neighbor) { // on the current neighbor lay part end(transition into neighbor Thick part) if (curr.support_in < half_support_distance) results.push_back(std::make_unique( - *part_end_it, &config, SupportIslandPoint::Type::center_line1)); + *part_end_it, &config, SupportIslandPoint::Type::thin_part)); curr.neighbor = nullptr; continue; } @@ -673,18 +578,18 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re // OLD function name was create_sample_center_end() // detect loop on island part - const Neighbor *twin = VoronoiGraphUtils::get_twin(*curr.neighbor); - if (auto process_it = std::find_if(process.begin(), process.end(), - [twin](const SupportIn &p) {return p.neighbor == twin; }); + const Neighbor *twin = VoronoiGraphUtils::get_twin(*curr.neighbor); + if (auto process_it = std::find_if(process.begin(), process.end(), + [twin](const SupportIn &p) { return p.neighbor == twin; }); process_it != process.end()) { // self loop detected - if (curr.support_in < half_support_distance){ + if (curr.support_in < half_support_distance) { Position position{curr.neighbor, 1.}; // fine tune position by alignment results.push_back(std::make_unique( - position, &config, SupportIslandPoint::Type::center_line1)); + position, &config, SupportIslandPoint::Type::thin_part_loop)); } - process.erase(process_it); + process.erase(process_it); curr.neighbor = nullptr; - continue; + continue; } // next neighbor is short cut to not push back and pop new_starts @@ -692,7 +597,7 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re for (const Neighbor &node_neighbor : curr.neighbor->node->neighbors) { // Check wheather node is not previous one if (twin == &node_neighbor) - continue; + continue; if (next_neighbor == nullptr) { next_neighbor = &node_neighbor; continue; @@ -769,29 +674,34 @@ outline_offset(const Slic3r::ExPolygon &island, float offset_delta) const Line &island_line = island_lines[island_line_index]; Vec2d dir1 = LineUtils::direction(island_line).cast(); dir1.normalize(); + size_t majorit_axis = (fabs(dir1.x()) > fabs(dir1.y())) ? 0 : 1; + coord_t start1 = island_line.a[majorit_axis]; + coord_t end1 = island_line.b[majorit_axis]; + if (start1 > end1) std::swap(start1, end1); + for (size_t offset_line_index = 0; offset_line_index < offset_lines.size(); ++offset_line_index) { const Line &offset_line = offset_lines[offset_line_index]; + + // check that line overlap its interval + coord_t start2 = offset_line.a[majorit_axis]; + coord_t end2 = offset_line.b[majorit_axis]; + if (start2 > end2) std::swap(start2, end2); + if (start1 > end2 || start2 > end1) continue; // not overlaped intervals + Vec2d dir2 = LineUtils::direction(offset_line).cast(); dir2.normalize(); - double angle = acos(dir1.dot(dir2)); - // not similar direction - - if (fabs(angle) > angle_tolerace) continue; + double angle = acos(dir1.dot(dir2)); + if (fabs(angle) > angle_tolerace) continue; // not similar direction + // Improve: use only one side of offest !! Point offset_middle = LineUtils::middle(offset_line); - Point island_middle = LineUtils::middle(island_line); - Point diff_middle = offset_middle - island_middle; - if (fabs(diff_middle.x()) > 2 * offset_delta || - fabs(diff_middle.y()) > 2 * offset_delta) - continue; - double distance = island_line.perp_distance_to(offset_middle); if (fabs(distance - offset_delta) > distance_tolerance) - continue; + continue; // only parallel line with big distance - // found offseted line + // found first offseted line converter[island_line_index] = offset_line_index; - break; + break; } } @@ -856,9 +766,12 @@ std::vector get_line_indices(const Neighbor* input, const Positions& end /// /// Fix expolygon with hole bigger than contour +/// NOTE: when change contour and index it is neccesary also fix source indices /// -/// In/Out expolygon -bool set_biggest_hole_as_contour(ExPolygon& shape){ +/// [In/Out] expolygon +/// [OUT] source indices of island contour line creating field +/// True when contour is changed +bool set_biggest_hole_as_contour(ExPolygon &shape, std::vector &ids) { Point contour_size = BoundingBox(shape.contour.points).size(); Polygons &holes = shape.holes; size_t contour_index = holes.size(); @@ -873,9 +786,28 @@ bool set_biggest_hole_as_contour(ExPolygon& shape){ return false; // contour is set correctly // some hole is bigger than contour and become contour + + // swap source indices + size_t contour_count = shape.contour.size(); + size_t hole_index_offset = contour_count; + for (size_t i = 0; i < contour_index; i++) + hole_index_offset += shape.holes[i].size(); + size_t hole_index_end = hole_index_offset + shape.holes[contour_index].size(); + + // swap contour with hole Polygon tmp = holes[contour_index]; // copy std::swap(tmp, shape.contour); holes[contour_index] = std::move(tmp); + + // Temp copy of the old hole(newly contour) indices + std::vector contour_indices(ids.begin() + hole_index_offset, + ids.begin() + hole_index_end); // copy + ids.erase(ids.begin() + hole_index_offset, // remove copied contour + ids.begin() + hole_index_end); + ids.insert(ids.begin() + hole_index_offset, // insert old contour(newly hole) + ids.begin(), ids.begin() + contour_count); + ids.erase(ids.begin(), ids.begin() + contour_count); // remove old contour + ids.insert(ids.begin(), contour_indices.begin(), contour_indices.end()); return true; } @@ -891,9 +823,9 @@ struct Field // same size as polygon.points.size() // indexes to source island lines // in case (index > lines.size()) it means fill the gap from tiny part of island - std::vector source_indexes; + std::vector source_indices; // value for source index of change from wide to tiny part of island - size_t source_indexe_for_change; + size_t source_index_for_change; // inner part of field ExPolygon inner; @@ -901,7 +833,44 @@ struct Field std::map field_2_inner; }; -void draw(SVG &svg, const Field &field, bool draw_border_line_indexes = false, bool draw_field_source_indexes = true); +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH +void draw(SVG &svg, const Field &field, bool draw_border_line_indexes = false, bool draw_field_source_indexes = true) { + const char *field_color = "red"; + const char *border_line_color = "blue"; + const char *inner_line_color = "green"; + const char *source_index_text_color = "blue"; + svg.draw(field.border, field_color); + Lines border_lines = to_lines(field.border); + LineUtils::draw(svg, border_lines, border_line_color, 0., draw_border_line_indexes); + if (draw_field_source_indexes) + for (auto &line : border_lines) { + size_t index = &line - &border_lines.front(); + // start of holes + if (index >= field.source_indices.size()) + break; + Point middle_point = LineUtils::middle(line); + std::string text = std::to_string(field.source_indices[index]); + auto item = field.field_2_inner.find(index); + if (item != field.field_2_inner.end()) { + text += " inner " + std::to_string(item->second); + } + svg.draw_text(middle_point, text.c_str(), source_index_text_color); + } + + if (field.inner.empty()) + return; + // draw inner + Lines inner_lines = to_lines(field.inner); + LineUtils::draw(svg, inner_lines, inner_line_color, 0., draw_border_line_indexes); + if (draw_field_source_indexes) + for (auto &line : inner_lines) { + size_t index = &line - &inner_lines.front(); + Point middle_point = LineUtils::middle(line); + std::string text = std::to_string(index); + svg.draw_text(middle_point, text.c_str(), inner_line_color); + } +} +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH // IMPROVE do not use pointers on node but pointers on Neighbor Field create_thick_field(const ThickPart& part, const Lines &lines, const SampleConfig &config) @@ -943,8 +912,8 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample std::map b_connection = LineUtils::create_line_connection_over_b(lines); - std::vector source_indexes; - auto inser_point_b = [&lines, &b_connection, &source_indexes] + std::vector source_indices; + auto inser_point_b = [&lines, &b_connection, &source_indices] (size_t &index, Points &points, std::set &done) { const Line &line = lines[index]; @@ -953,10 +922,10 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample assert(connection_item != b_connection.end()); done.insert(index); index = connection_item->second; - source_indexes.push_back(index); + source_indices.push_back(index); }; - size_t source_indexe_for_change = lines.size(); + size_t source_index_for_change = lines.size(); /// /// Insert change into @@ -965,7 +934,7 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample /// island(ExPolygon) converted to lines /// ... /// False when change lead to close loop(into first change) otherwise True - auto insert_changes = [&wide_tiny_changes, &lines, &source_indexes, source_indexe_for_change] + auto insert_changes = [&wide_tiny_changes, &lines, &source_indices, source_index_for_change] (size_t &index, Points &points, std::set &done, size_t input_index)->bool { auto change_item = wide_tiny_changes.find(index); while (change_item != wide_tiny_changes.end()) { @@ -996,15 +965,15 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample if (points.empty() || !PointUtils::is_equal(points.back(), change.new_b)) { points.push_back(change.new_b); - source_indexes.push_back(source_indexe_for_change); + source_indices.push_back(source_index_for_change); } else { - source_indexes.back() = source_indexe_for_change; + source_indices.back() = source_index_for_change; } // prevent double points if (!PointUtils::is_equal(lines[change.next_line_index].b, change.next_new_a)) { points.push_back(change.next_new_a); - source_indexes.push_back(change.next_line_index); + source_indices.push_back(change.next_line_index); } done.insert(index); @@ -1052,33 +1021,33 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample size_t input_index = std::min(input_index1, input_index2); // Why select min index? size_t outline_index = input_index; // Done indexes is used to detect holes in field - std::set done_indexes; // IMPROVE: use vector(size of lines count) with bools + std::set done_indices; // IMPROVE: use vector(size of lines count) with bools do { - if (!insert_changes(outline_index, points, done_indexes, input_index)) + if (!insert_changes(outline_index, points, done_indices, input_index)) break; - inser_point_b(outline_index, points, done_indexes); + inser_point_b(outline_index, points, done_indices); } while (outline_index != input_index); assert(points.size() >= 3); Field field; field.border.contour = Polygon(points); // finding holes(another closed polygon) - if (done_indexes.size() < field_line_indices.size()) { + if (done_indices.size() < field_line_indices.size()) { for (const size_t &index : field_line_indices) { - if(done_indexes.find(index) != done_indexes.end()) continue; + if(done_indices.find(index) != done_indices.end()) continue; // new hole Points hole_points; size_t hole_index = index; do { - inser_point_b(hole_index, hole_points, done_indexes); + inser_point_b(hole_index, hole_points, done_indices); } while (hole_index != index); field.border.holes.emplace_back(hole_points); } // Set largest polygon as contour - set_biggest_hole_as_contour(field.border); + set_biggest_hole_as_contour(field.border, source_indices); } - field.source_indexe_for_change = source_indexe_for_change; - field.source_indexes = std::move(source_indexes); + field.source_index_for_change = source_index_for_change; + field.source_indices = std::move(source_indices); std::tie(field.inner, field.field_2_inner) = outline_offset(field.border, (float)config.minimal_distance_from_outline); #ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH @@ -1219,31 +1188,24 @@ Slic3r::Points sample_expolygon_with_centering(const ExPolygon &expoly, coord_t /// Input field /// Parameters for sampling. /// support for outline -SupportIslandPoints sample_outline( - const Field &field, const SampleConfig &config) -{ - const ExPolygon &border = field.border; +SupportIslandPoints sample_outline(const Field &field, const SampleConfig &config){ + const ExPolygon &border = field.border; const Polygon &contour = border.contour; - assert(field.source_indexes.size() >= contour.size()); + assert(field.source_indices.size() >= contour.size()); coord_t max_align_distance = config.max_align_distance; coord_t sample_distance = config.outline_sample_distance; SupportIslandPoints result; using RestrictionPtr = std::shared_ptr; auto add_sample = [&](size_t index, const RestrictionPtr& restriction, coord_t &last_support) { - using Position = SupportOutlineIslandPoint::Position; const double &line_length_double = restriction->lengths[index]; - coord_t line_length = static_cast(std::round(line_length_double)); - if (last_support + line_length > sample_distance) { - do { - float ratio = static_cast((sample_distance - last_support) / line_length_double); - result.emplace_back( - std::make_unique( - Position(index, ratio), restriction, - SupportIslandPoint::Type::outline) - ); - last_support -= sample_distance; - } while (last_support + line_length > sample_distance); + coord_t line_length = static_cast(std::round(line_length_double)); + while (last_support + line_length > sample_distance){ + float ratio = static_cast((sample_distance - last_support) / line_length_double); + SupportOutlineIslandPoint::Position position(index, ratio); + result.emplace_back(std::make_unique( + position, restriction, SupportIslandPoint::Type::thick_part_outline)); + last_support -= sample_distance; } last_support += line_length; }; @@ -1258,14 +1220,12 @@ SupportIslandPoints sample_outline( sum_lengths += length; lengths.push_back(length); } - // no samples on this polygon using Restriction = SupportOutlineIslandPoint::RestrictionCircleSequence; auto restriction = std::make_shared(lines, lengths, max_align_distance); coord_t last_support = std::min(static_cast(sum_lengths), sample_distance) / 2; - for (size_t index = 0; index < lines.size(); ++index) { + for (size_t index = 0; index < lines.size(); ++index) add_sample(index, restriction, last_support); - } }; // sample line sequence @@ -1312,14 +1272,13 @@ SupportIslandPoints sample_outline( add_sample(index, restriction, last_support); } }; - - - // convert map from field index to inner(line index) - const std::map& field_2_inner = field.field_2_inner; - const size_t& change_index = field.source_indexe_for_change; - auto sample_polygon = [&](const Polygon &polygon, - const Polygon &inner_polygon, - size_t index_offset) { + + // convert map from field index to inner(line index) + auto sample_polygon = [&add_circle_sample, &add_lines_samples, &field] + (const Polygon &polygon, const Polygon &inner_polygon, size_t index_offset) { + const std::vector &source_indices = field.source_indices; + const size_t& change_index = field.source_index_for_change; + const std::map &field_2_inner = field.field_2_inner; if (inner_polygon.empty()) return; // nothing to sample @@ -1327,8 +1286,8 @@ SupportIslandPoints sample_outline( size_t first_change_index = polygon.size(); for (size_t polygon_index = 0; polygon_index < polygon.size(); ++polygon_index) { size_t index = polygon_index + index_offset; - assert(index < field.source_indexes.size()); - size_t source_index = field.source_indexes[index]; + assert(index < source_indices.size()); + size_t source_index = source_indices[index]; if (source_index == change_index) { // found change from wide to tiny part first_change_index = polygon_index; @@ -1353,8 +1312,8 @@ SupportIslandPoints sample_outline( polygon_index != stop_index; ++polygon_index) { if (polygon_index == polygon.size()) polygon_index = 0; size_t index = polygon_index + index_offset; - assert(index < field.source_indexes.size()); - size_t source_index = field.source_indexes[index]; + assert(index < source_indices.size()); + size_t source_index = source_indices[index]; if (source_index == change_index) { if (inner_first == inner_invalid) continue; // create Restriction object @@ -1419,7 +1378,7 @@ void create_supports_for_thick_part(const ThickPart &part, SupportIslandPoints & std::transform(inner_points.begin(), inner_points.end(), std::back_inserter(results), [&](const Point &point) { return std::make_unique( - point, inner, SupportIslandPoint::Type::inner); + point, inner, SupportIslandPoint::Type::thick_part_inner); }); } @@ -1463,14 +1422,14 @@ using IslandParts = std::vector; /// struct ProcessItem { // previously processed island node - const VoronoiGraph::Node *prev_node; + const VoronoiGraph::Node *prev_node = nullptr; // current island node to investigate neighbors - const VoronoiGraph::Node *node; + const VoronoiGraph::Node *node = nullptr; // index of island part stored in island_parts // NOTE: Can't use reference because of vector reallocation - size_t i; + size_t i = std::numeric_limits::max(); }; using ProcessItems = std::vector; @@ -2109,7 +2068,7 @@ std::pair separate_thin_thick( ProcessItems process; // queue of nodes to process do { // iterate over all nodes in graph and collect interfaces into island_parts assert(item.node != nullptr); - ProcessItem next_item{nullptr, nullptr, -1}; + ProcessItem next_item = {nullptr, nullptr, std::numeric_limits::max()}; for (const Neighbor &neighbor: item.node->neighbors) { if (neighbor.node == item.prev_node) continue; // already done if (next_item.node != nullptr) // already prepared item is stored into queue @@ -2160,130 +2119,42 @@ std::pair separate_thin_thick( /// Maximal distance from side /// 2x Static Support point(lay os sides of path) SupportIslandPoints create_side_points( - const VoronoiGraph::Nodes &path, - const Lines& lines, - coord_t width, - coord_t max_side_distance) + const VoronoiGraph::ExPath &path, const Lines& lines, const SampleConfig &config, + SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points) { - VoronoiGraph::Nodes reverse_path = path; // copy + coord_t max_distance_by_length = static_cast(path.length * config.max_length_ratio_for_two_support_points); + coord_t max_distance = std::min(config.maximal_distance_from_outline, max_distance_by_length); + + VoronoiGraph::Nodes reverse_path = path.nodes; // copy std::reverse(reverse_path.begin(), reverse_path.end()); - coord_t side_distance1 = max_side_distance; // copy - coord_t side_distance2 = max_side_distance; // copy - auto pos1 = create_position_on_path(path, lines, width, side_distance1); + + coord_t width = 2 * config.head_radius; + coord_t side_distance1 = max_distance; // copy + coord_t side_distance2 = max_distance; // copy + auto pos1 = create_position_on_path(path.nodes, lines, width, side_distance1); auto pos2 = create_position_on_path(reverse_path, lines, width, side_distance2); assert(pos1.has_value()); assert(pos2.has_value()); - SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points; - SupportIslandPoints result; + SupportIslandPoints result; result.reserve(2); result.push_back(create_no_move_point(*pos1, type)); result.push_back(create_no_move_point(*pos2, type)); return result; } -SupportIslandPoints sample_expath( - const VoronoiGraph::ExPath &path, - const Lines & lines, - const SampleConfig & config) -{ - // 1) One support point - if (path.length < config.max_length_for_one_support_point) { - // create only one point in center - SupportIslandPoints result; - result.push_back(create_middle_path_point( - path, SupportIslandPoint::Type::one_center_point)); - return result; - } - - double max_width = VoronoiGraphUtils::get_max_width(path); - if (max_width < config.max_width_for_center_support_line) { - // 2) Two support points have to stretch island even if haed is not fully under island. - if (path.length < config.max_length_for_two_support_points) { - coord_t max_distance_by_length = static_cast(path.length / 4); - coord_t max_distance = std::min(config.maximal_distance_from_outline, max_distance_by_length); - - // Be carefull tiny island could contain overlapped support points - assert(max_distance < (static_cast(path.length / 2) - config.head_radius)); - - coord_t min_width = 2 * config.head_radius; //minimal_distance_from_outline; - return create_side_points(path.nodes, lines, min_width, config.maximal_distance_from_outline); - } - - // othewise sample path - /*CenterLineConfiguration - centerLineConfiguration(path.side_branches, config); - SupportIslandPoints samples = sample_center_line(path, centerLineConfiguration); - samples.front()->type = SupportIslandPoint::Type::center_line_end2; - return samples;*/ - } - - // TODO: 3) Triangle of points - // eval outline and find three point create almost equilateral triangle - - // 4) Thin and thick support - SupportIslandPoints result; - auto [thin, thick] = separate_thin_thick(path, lines, config); - for (const ThinPart &part : thin) create_supports_for_thin_part(part, result, config); - for (const ThickPart &part : thick) create_supports_for_thick_part(part, result, lines, config); - return result; -} - -void draw(SVG &svg, const Field &field, bool draw_border_line_indexes, bool draw_field_source_indexes) { - const char *field_color = "red"; - const char *border_line_color = "blue"; - const char *inner_line_color = "green"; - const char *source_index_text_color = "blue"; - svg.draw(field.border, field_color); - Lines border_lines = to_lines(field.border); - LineUtils::draw(svg, border_lines, border_line_color, 0., draw_border_line_indexes); - if (draw_field_source_indexes) - for (auto &line : border_lines) { - size_t index = &line - &border_lines.front(); - // start of holes - if (index >= field.source_indexes.size()) - break; - Point middle_point = LineUtils::middle(line); - std::string text = std::to_string(field.source_indexes[index]); - auto item = field.field_2_inner.find(index); - if (item != field.field_2_inner.end()) { - text += " inner " + std::to_string(item->second); - } - svg.draw_text(middle_point, text.c_str(), source_index_text_color); - } - - if (field.inner.empty()) - return; - // draw inner - Lines inner_lines = to_lines(field.inner); - LineUtils::draw(svg, inner_lines, inner_line_color, 0., draw_border_line_indexes); - if (draw_field_source_indexes) - for (auto &line : inner_lines) { - size_t index = &line - &inner_lines.front(); - Point middle_point = LineUtils::middle(line); - std::string text = std::to_string(index); - svg.draw_text(middle_point, text.c_str(), inner_line_color); - } -} - void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radius, bool write_type) { const char *color = nullptr; for (const auto &p : supportIslandPoints) { switch (p->type) { - case SupportIslandPoint::Type::center_line1: - case SupportIslandPoint::Type::center_line2: - case SupportIslandPoint::Type::center_line3: - case SupportIslandPoint::Type::center_circle: - case SupportIslandPoint::Type::center_circle_end: - case SupportIslandPoint::Type::center_circle_end2: - case SupportIslandPoint::Type::center_line_end: - case SupportIslandPoint::Type::center_line_end2: - case SupportIslandPoint::Type::center_line_end3: - case SupportIslandPoint::Type::center_line_start: color = "lightred"; break; - case SupportIslandPoint::Type::outline: color = "lightblue"; break; - case SupportIslandPoint::Type::inner: color = "lightgreen"; break; + case SupportIslandPoint::Type::thin_part: + case SupportIslandPoint::Type::thin_part_change: + case SupportIslandPoint::Type::thin_part_loop: color = "lightred"; break; + case SupportIslandPoint::Type::thick_part_outline: color = "lightblue"; break; + case SupportIslandPoint::Type::thick_part_inner: color = "lightgreen"; break; case SupportIslandPoint::Type::one_bb_center_point: color = "red"; break; case SupportIslandPoint::Type::one_center_point: case SupportIslandPoint::Type::two_points: + case SupportIslandPoint::Type::two_points_backup: default: color = "black"; } svg.draw(p->point, color, radius); @@ -2294,9 +2165,136 @@ void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radi } } } + } // namespace -bool SampleIslandUtils::is_uniform_cover_island_visualization_disabled() { +////////////////////////////// +/// uniform support island /// +////////////////////////////// +namespace Slic3r::sla { +SupportIslandPoints uniform_support_island(const ExPolygon &island, const SampleConfig &config){ + ExPolygon simplified_island = get_simplified(island, config); +#ifdef OPTION_TO_STORE_ISLAND + std::string path; + if (!config.path.empty()) { + static int counter = 0; + path = replace_first(config.path, "<>", std::to_string(++counter)); + draw_island(path, island, simplified_island); + // need to save svg in case of infinite loop so no store SVG into variable + } +#endif // OPTION_TO_STORE_ISLAND + + // 0) When island is smaller than minimal-head diameter, + // it will be supported whole by support poin in center + if (Point center; get_center(simplified_island.contour.points, config.head_radius, center)) { + SupportIslandPoints supports; + supports.push_back(std::make_unique( + center, SupportIslandInnerPoint::Type::one_bb_center_point)); +#ifdef OPTION_TO_STORE_ISLAND + if (!path.empty()){ // add center support point into image + SVG svg = draw_island(path, island, simplified_island); + draw(svg, supports, config.head_radius); + } +#endif // OPTION_TO_STORE_ISLAND + return supports; + } + + Slic3r::Geometry::VoronoiDiagram vd; + Lines lines = to_lines(simplified_island); + vd.construct_voronoi(lines.begin(), lines.end()); + Slic3r::Voronoi::annotate_inside_outside(vd, lines); + VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines); + VoronoiGraph::ExPath longest_path; + + const VoronoiGraph::Node *start_node = VoronoiGraphUtils::getFirstContourNode(skeleton); + // every island has to have a point on contour + assert(start_node != nullptr); + longest_path = VoronoiGraphUtils::create_longest_path(start_node); + +#ifdef OPTION_TO_STORE_ISLAND // add voronoi diagram with longest path into image + if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config); +#endif // OPTION_TO_STORE_ISLAND + + // 1) One support point + if (longest_path.length < config.max_length_for_one_support_point) { + // create only one point in center + SupportIslandPoints supports; + supports.push_back(create_middle_path_point( + longest_path, SupportIslandPoint::Type::one_center_point)); +#ifdef OPTION_TO_STORE_ISLAND + if (!path.empty()){ + SVG svg = draw_island(path, island, simplified_island); + draw(svg, supports, config.head_radius); + } +#endif // OPTION_TO_STORE_ISLAND + return supports; + } + + // 2) Two support points have to stretch island even if haed is not fully under island. + if (VoronoiGraphUtils::get_max_width(longest_path) < config.max_width_for_center_support_line && + longest_path.length < config.max_length_for_two_support_points) { + SupportIslandPoints supports = create_side_points(longest_path, lines, config); +#ifdef OPTION_TO_STORE_ISLAND + if (!path.empty()){ + SVG svg = draw_island(path, island, simplified_island); + draw(svg, supports, config.head_radius); + } +#endif // OPTION_TO_STORE_ISLAND + return supports; + } + + // TODO: 3) Triangle aligned support points + // eval outline and find three point create almost equilateral triangle to stretch island + + // 4) Divide island on Thin & Thick part and support by parts + SupportIslandPoints supports; + auto [thin, thick] = separate_thin_thick(longest_path, lines, config); + assert(!thin.empty() || !thick.empty()); + for (const ThinPart &part : thin) create_supports_for_thin_part(part, supports, config); + for (const ThickPart &part : thick) create_supports_for_thick_part(part, supports, lines, config); + + // At least 2 support points are neccessary after thin/thick sampling heuristic + if (supports.size() <= 2){ + SupportIslandInnerPoint::Type type = SupportIslandInnerPoint::Type::two_points_backup; + SupportIslandPoints two_supports = create_side_points(longest_path, lines, config, type); +#ifdef OPTION_TO_STORE_ISLAND + if (!path.empty()) { + SVG svg = draw_island(path, island, simplified_island); + draw(svg, two_supports, config.head_radius); + } +#endif // OPTION_TO_STORE_ISLAND + return two_supports; + } + +#ifdef OPTION_TO_STORE_ISLAND + Points supports_before_align = ::to_points(supports); + if (!path.empty()) { + SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config); + draw(svg, supports, config.head_radius); + } +#endif // OPTION_TO_STORE_ISLAND + + // allign samples + align_samples(supports, island, config); +#ifdef OPTION_TO_STORE_ISLAND + if (!path.empty()) { + SVG svg = draw_island(path, island, simplified_island); + coord_t width = config.head_radius / 5; + VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "darkorange"); + VoronoiGraphUtils::draw(svg, skeleton, lines, config, false /*print Pointer address*/); + + Lines align_moves; + align_moves.reserve(supports.size()); + for (size_t i = 0; i < supports.size(); ++i) + align_moves.push_back(Line(supports[i]->point, supports_before_align[i])); + svg.draw(align_moves, "lightgray", width); + draw(svg, supports, config.head_radius); + } +#endif // OPTION_TO_STORE_ISLAND + return supports; +} + +bool is_uniform_support_island_visualization_disabled() { #ifndef NDEBUG return false; #endif @@ -2311,3 +2309,5 @@ bool SampleIslandUtils::is_uniform_cover_island_visualization_disabled() { #endif return true; } + +} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp b/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp index 1e4c96ca09..fb0cf2309a 100644 --- a/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp +++ b/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp @@ -1,5 +1,5 @@ -#ifndef slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ -#define slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ +#ifndef slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_ +#define slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_ #include #include "SampleConfig.hpp" @@ -8,26 +8,17 @@ namespace Slic3r::sla { /// -/// Utils class with only static function -/// Function for sampling island by Voronoi Graph. +/// Distribute support points across island area defined by ExPolygon. /// -class SampleIslandUtils -{ -public: - SampleIslandUtils() = delete; +/// Shape of island +/// Configuration of support density +/// Support points laying inside of island +SupportIslandPoints uniform_support_island(const ExPolygon &island, const SampleConfig &config); - /// - /// Main entry for sample island - /// - /// Shape of island - /// Configuration for sampler - /// List of support points - static SupportIslandPoints uniform_cover_island( - const ExPolygon &island, const SampleConfig &config); - - - static bool is_uniform_cover_island_visualization_disabled(); -}; +/// +/// Check for tests that developer do not forget disable visualization after debuging. +/// +bool is_uniform_support_island_visualization_disabled(); } // namespace Slic3r::sla -#endif // slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ +#endif // slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_ diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index ba05a4497f..53870116e1 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -8,9 +8,10 @@ #include "libslic3r/Execution/ExecutionTBB.hpp" // parallel preparation of data for sampling #include "libslic3r/Execution/Execution.hpp" #include "libslic3r/KDTreeIndirect.hpp" +#include "libslic3r/ClipperUtils.hpp" // SupportIslands -#include "libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp" +#include "libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp" using namespace Slic3r; using namespace Slic3r::sla; @@ -145,7 +146,7 @@ private: /// Circle center point /// squared value of Circle Radius (r2 = r*r) /// Intersection point -Point intersection(const Point &p1, const Point &p2, const Point &cnt, double r2) { +Point intersection_line_circle(const Point &p1, const Point &p2, const Point &cnt, double r2) { // Vector from p1 to p2 Vec2d dp_d((p2 - p1).cast()); // Vector from circle center to p1 @@ -269,7 +270,7 @@ void support_part_overhangs( /// void support_island(const LayerPart &part, NearPoints& near_points, float part_z, const SupportPointGeneratorConfig &cfg) { - SupportIslandPoints samples = SampleIslandUtils::uniform_cover_island(*part.shape, cfg.island_configuration); + SupportIslandPoints samples = uniform_support_island(*part.shape, cfg.island_configuration); //samples = {std::make_unique(island.contour.centroid())}; for (const SupportIslandPointPtr &sample : samples) near_points.add(LayerSupportPoint{ @@ -337,7 +338,7 @@ Slic3r::Points sample(Points::const_iterator b, Points::const_iterator e, double while (p_dist2 > dist2) { // line segment goes out of radius if (prev_pt == nullptr) prev_pt = &(*it); - r.push_back(intersection(*prev_pt, pt, r.back(), dist2)); + r.push_back(intersection_line_circle(*prev_pt, pt, r.back(), dist2)); p_dist2 = (r.back() - pt).cast().squaredNorm(); prev_pt = &r.back(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index cf8640a9a1..58fce55160 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -838,11 +838,9 @@ void GLGizmoSlaSupports::draw_island_config() { if (float max_distance = unscale(sample_config.max_distance); // [in mm] ImGui::InputFloat("Max dist", &max_distance, .1f, 1.f, "%.2f mm")) { sample_config.max_distance = scale_(max_distance); - sample_config.half_distance = sample_config.max_distance / 2; exist_change = true; } else if (ImGui::IsItemHovered()) ImGui::SetTooltip("Every support point on island has at least one support point in maximum distance\nMUST be bigger than zero"); - ImGui::SameLine(); ImGui::Text("half is %.2f", unscale(sample_config.half_distance)); if (float minimal_distance_from_outline = unscale(sample_config.minimal_distance_from_outline); // [in mm] ImGui::InputFloat("from outline", &minimal_distance_from_outline, .1f, 1.f, "%.2f mm")) { sample_config.minimal_distance_from_outline = scale_(minimal_distance_from_outline); @@ -856,20 +854,6 @@ void GLGizmoSlaSupports::draw_island_config() { exist_change = true; } else if (ImGui::IsItemHovered()) ImGui::SetTooltip("Measured as sum of VD edge length from outline\nUsed only when there is no space for outline offset on first/last point\nMust be bigger than minimal_distance_from_outline"); - ImGui::Text("max_interesting_angle is %.0f", float(sample_config.max_interesting_angle*180/M_PI)); - if (ImGui::IsItemHovered()) ImGui::SetTooltip(" When angle on outline is smaller than max_interesting_angle\nthan create unmovable support point.\nShould be in range from 90 to 180"); - if (float minimal_support_distance = unscale(sample_config.minimal_support_distance); // [in mm] - ImGui::InputFloat("Thin dist", &minimal_support_distance, .1f, 1.f, "%.2f mm")) { - sample_config.minimal_support_distance = scale_(minimal_support_distance); - exist_change = true; - } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Distinguish when to add support point on Voronoi Diagram\nMUST be bigger than minimal_distance_from_outline\nSmaller -> more supports AND Larger -> less amount"); - if (float min_side_branch_length = unscale(sample_config.min_side_branch_length); // [in mm] - ImGui::InputFloat("min_side_branch_length", &min_side_branch_length, .1f, 1.f, "%.2f mm")) { - sample_config.min_side_branch_length = scale_(min_side_branch_length); - exist_change = true; - } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("minimal length of side branch to be sampled\nit is used for sampling in center only"); if (float max_for_one = unscale(sample_config.max_length_for_one_support_point); // [in mm] ImGui::InputFloat("Max len for one", &max_for_one, .1f, 1.f, "%.2f mm")) { sample_config.max_length_for_one_support_point = scale_(max_for_one); diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 8dd8c76075..64567ef8d1 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include "nanosvg/nanosvg.h" // load SVG file #include "sla_test_utils.hpp" @@ -357,7 +357,10 @@ ExPolygons createTestIslands(double size) bool useFrogLeg = false; // need post reorganization of longest path ExPolygons result = { - load_svg(dir + "lm_issue.svg"), // change from thick to thin and vice versa on circle + + // debug + create_tiny_between_holes(3 * size, 2 / 3. * size), + // one support point ExPolygon(PolygonUtils::create_equilateral_triangle(size)), ExPolygon(PolygonUtils::create_square(size)), @@ -389,6 +392,7 @@ ExPolygons createTestIslands(double size) ExPolygon(PolygonUtils::create_equilateral_triangle(scale_(18.6))), create_cylinder_bottom_slice(), + load_svg(dir + "lm_issue.svg"), // change from thick to thin and vice versa on circle // still problem // three support points @@ -450,7 +454,7 @@ Points rasterize(const ExPolygon &island, double distance) { SupportIslandPoints test_island_sampling(const ExPolygon & island, const SampleConfig &config) { - auto points = SampleIslandUtils::uniform_cover_island(island, config); + auto points = uniform_support_island(island, config); Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer bool is_ok = true; @@ -508,12 +512,9 @@ SampleConfig create_sample_config(double size) { SampleConfig cfg; cfg.max_distance = 3 * size + 0.1; - cfg.half_distance = cfg.max_distance/2; cfg.head_radius = size / 4; cfg.minimal_distance_from_outline = cfg.head_radius; cfg.maximal_distance_from_outline = cfg.max_distance/4; - cfg.min_side_branch_length = 2 * cfg.minimal_distance_from_outline; - cfg.minimal_support_distance = cfg.minimal_distance_from_outline + cfg.half_distance; cfg.max_length_for_one_support_point = 2*size; cfg.max_length_for_two_support_points = 4*size; cfg.max_width_for_center_support_line = size; @@ -524,7 +525,7 @@ SampleConfig create_sample_config(double size) { cfg.count_iteration = 100; cfg.max_align_distance = 0; return cfg; -} +} #ifdef STORE_SAMPLE_INTO_SVG_FILES namespace { @@ -590,6 +591,6 @@ TEST_CASE("Disable visualization", "[hide]") #ifdef STORE_SAMPLE_INTO_SVG_FILES CHECK(false); #endif // STORE_SAMPLE_INTO_SVG_FILES - CHECK(SampleIslandUtils::is_visualization_disabled()); + CHECK(is_uniform_support_island_visualization_disabled()); }