From 2debc31c4dcdeb72c2bb60651a7156e2f3e57234 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 13 Feb 2025 17:52:06 +0800 Subject: [PATCH] ENH: fixed crash issue of lightning infill of tree support Use clipper2 and remove colinear points. Later on we can try to set lightning infill as the default infill pattern of tree support. jira: none Change-Id: Id545b15d778071cec6e56d212ab68db1ab90ad2a --- src/libslic3r/Clipper2Utils.cpp | 53 +++++++++++- src/libslic3r/Clipper2Utils.hpp | 6 +- src/libslic3r/ExPolygon.cpp | 12 +++ src/libslic3r/ExPolygon.hpp | 2 + .../Fill/Lightning/DistanceField.cpp | 4 +- src/libslic3r/MultiPoint.cpp | 15 ++++ src/libslic3r/MultiPoint.hpp | 1 + src/libslic3r/Support/TreeSupport.cpp | 84 ++++++++++--------- 8 files changed, 131 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/Clipper2Utils.cpp b/src/libslic3r/Clipper2Utils.cpp index 143728caa..2cced955a 100644 --- a/src/libslic3r/Clipper2Utils.cpp +++ b/src/libslic3r/Clipper2Utils.cpp @@ -79,6 +79,14 @@ static ExPolygons PolyTreeToExPolygons(Clipper2Lib::PolyTree64 &&polytree) return retval; } +void SimplifyPolyTree(const Clipper2Lib::PolyPath64 &polytree, double epsilon, Clipper2Lib::PolyPath64 &result) +{ + for (const auto &child : polytree) { + Clipper2Lib::PolyPath64 *newchild = result.AddChild(Clipper2Lib::SimplifyPath(child->Polygon(), epsilon)); + SimplifyPolyTree(*child, epsilon, *newchild); + } +} + Clipper2Lib::Paths64 Slic3rPolygons_to_Paths64(const Polygons &in) { Clipper2Lib::Paths64 out; @@ -132,7 +140,7 @@ Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip) { return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); } -ExPolygons union_ex2(const Polygons& polygons) +ExPolygons union_ex_2(const Polygons& polygons) { Clipper2Lib::Clipper64 c; c.AddSubject(Slic3rPolygons_to_Paths64(polygons)); @@ -147,7 +155,7 @@ ExPolygons union_ex2(const Polygons& polygons) return results; } -ExPolygons union_ex2(const ExPolygons &expolygons) +ExPolygons union_ex_2(const ExPolygons &expolygons) { Clipper2Lib::Clipper64 c; c.AddSubject(Slic3rExPolygons_to_Paths64(expolygons)); @@ -161,4 +169,43 @@ ExPolygons union_ex2(const ExPolygons &expolygons) return results; } -} \ No newline at end of file + +// 对 ExPolygons 进行偏移 +ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta) +{ + Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons); + Clipper2Lib::ClipperOffset offsetter; + offsetter.AddPaths(subject, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon); + Clipper2Lib::PolyPath64 polytree; + offsetter.Execute(delta, polytree); + ExPolygons results = PolyTreeToExPolygons(std::move(polytree)); + + return results; +} + +ExPolygons offset2_ex_2(const ExPolygons& expolygons, double delta1, double delta2) +{ + // 1st offset + Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons); + Clipper2Lib::ClipperOffset offsetter; + offsetter.AddPaths(subject, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon); + Clipper2Lib::PolyPath64 polytree; + offsetter.Execute(delta1, polytree); + + // simplify the result + Clipper2Lib::PolyPath64 polytree2; + SimplifyPolyTree(polytree, SCALED_EPSILON, polytree2); + + // 2nd offset + offsetter.Clear(); + offsetter.AddPaths(Clipper2Lib::PolyTreeToPaths64(polytree2), Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon); + polytree.Clear(); + offsetter.Execute(delta2, polytree); + + // convert back to expolygons + ExPolygons results = PolyTreeToExPolygons(std::move(polytree)); + + return results; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/Clipper2Utils.hpp b/src/libslic3r/Clipper2Utils.hpp index cd6e209fa..f0756a238 100644 --- a/src/libslic3r/Clipper2Utils.hpp +++ b/src/libslic3r/Clipper2Utils.hpp @@ -8,8 +8,10 @@ namespace Slic3r { Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip); Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip); -ExPolygons union_ex2(const Polygons &expolygons); -ExPolygons union_ex2(const ExPolygons &expolygons); +ExPolygons union_ex_2(const Polygons &expolygons); +ExPolygons union_ex_2(const ExPolygons &expolygons); +ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta); +ExPolygons offset2_ex_2(const ExPolygons &expolygons, double delta1, double delta2); } #endif diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 4d2142037..6b8761520 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -413,6 +413,18 @@ Lines ExPolygon::lines() const return lines; } +bool ExPolygon::remove_colinear_points() { + bool removed = this->contour.remove_colinear_points(); + if (contour.size() < 3) { + contour.points.clear(); + holes.clear(); + return true; + } + for (Polygon &hole : this->holes) + removed |= hole.remove_colinear_points(); + return removed; +} + // Do expolygons match? If they match, they must have the same topology, // however their contours may be rotated. bool expolygons_match(const ExPolygon &l, const ExPolygon &r) diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 1bffb080d..2f28792e8 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -76,6 +76,8 @@ public: { Polylines out; this->medial_axis(min_width, max_width, &out); return out; } Lines lines() const; + bool remove_colinear_points(); + // Number of contours (outer contour with holes). size_t num_contours() const { return this->holes.size() + 1; } Polygon& contour_or_hole(size_t idx) { return (idx == 0) ? this->contour : this->holes[idx - 1]; } diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index 65da94b83..b0a702a24 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -4,6 +4,7 @@ #include "DistanceField.hpp" //Class we're implementing. #include "../FillRectilinear.hpp" #include "../../ClipperUtils.hpp" +#include "../../Clipper2Utils.hpp" #include @@ -43,7 +44,8 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl m_supporting_radius2 = Slic3r::sqr(int64_t(radius)); // Sample source polygons with a regular grid sampling pattern. const BoundingBox overhang_bbox = get_extents(current_overhang); - ExPolygons expolys = offset2_ex(union_ex(current_overhang), -m_cell_size / 2, m_cell_size / 2); // remove dangling lines which causes sample_grid_pattern crash (fails the OUTER_LOW assertions) + // remove dangling lines which causes sample_grid_pattern crash (fails the OUTER_LOW assertions) + ExPolygons expolys = offset2_ex_2(union_ex_2(current_overhang), -m_cell_size / 2, m_cell_size / 2); for (const ExPolygon &expoly : expolys) { const Points sampled_points = sample_grid_pattern(expoly, m_cell_size, overhang_bbox); const size_t unsupported_points_prev_size = m_unsupported_points.size(); diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 0076300ea..6a5f309fe 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -118,6 +118,21 @@ bool MultiPoint::remove_duplicate_points() return false; } +bool MultiPoint::remove_colinear_points() { + if (points.size() < 3) return false; + bool changed = false; + for (size_t i = 1; i < points.size() - 1; ) { + if (Line::distance_to_infinite_squared(points[i], points[i - 1], points[i+1]) < SCALED_EPSILON) { + points.erase(points.begin() + i); + changed = true; + } else { + ++ i; + } + } + if (points.size() < 3) points.clear(); + return changed; +} + bool MultiPoint::intersection(const Line& line, Point* intersection) const { Lines lines = this->lines(); diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 4c1f9049e..cefce6f7d 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -77,6 +77,7 @@ public: bool has_duplicate_points() const; // Remove exact duplicates, return true if any duplicate has been removed. bool remove_duplicate_points(); + bool remove_colinear_points(); void clear() { this->points.clear(); } void append(const Point &point) { this->points.push_back(point); } void append(const Points &src) { this->append(src.begin(), src.end()); } diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 4bf42fc31..3ddac70a0 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2,6 +2,7 @@ #include #include "format.hpp" +#include "BuildVolume.hpp" #include "ClipperUtils.hpp" #include "Clipper2Utils.hpp" #include "Fill/FillBase.hpp" @@ -25,6 +26,13 @@ #include #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 @@ -605,7 +613,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p SupportMaterialPattern support_pattern = m_object_config->support_base_pattern; if (m_support_params.support_style == smsTreeHybrid && support_pattern == smpDefault) - support_pattern = smpRectilinear; + support_pattern = smpRectilinear;//smpLightning; if(support_pattern == smpLightning) m_support_params.base_fill_pattern = ipLightning; @@ -622,7 +630,8 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p m_ts_data = m_object->alloc_tree_support_preview_cache(); top_z_distance = m_object_config->support_top_z_distance.value; - if (top_z_distance > EPSILON) top_z_distance = std::max(top_z_distance, float(m_slicing_params.min_layer_height)); + if (top_z_distance > EPSILON) + top_z_distance = std::max(top_z_distance, float(m_slicing_params.min_layer_height)); #if USE_TREESUPPRT3D m_support_params.independent_layer_height = false; // do not use independent layer height for 3D tree support #endif @@ -956,7 +965,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted); Polygons cluster_boundary = to_polygons(cluster_boundary_ex); if (cluster_boundary.empty()) continue; - + for (auto &pt : poly.contour.points) { double dist_pt = std::numeric_limits::max(); for (auto &ply : cluster_boundary) { @@ -1589,7 +1598,7 @@ void TreeSupport::generate_toolpaths() if (area_group.type == SupportLayer::Roof1stLayer) { // use higher flow for roof_1st_layer ? Flow roof_1st_layer_flow = support_material_interface_flow(m_object, ts_layer->height)/*.with_flow_ratio(1.5)*/; - + // Note: spacing means the separation between two lines as if they are tightly extruded filler_Roof1stLayer->spacing = roof_1st_layer_flow.spacing(); // generate a perimeter first to support interface better @@ -1629,9 +1638,9 @@ void TreeSupport::generate_toolpaths() if(m_object_config->support_base_pattern==smpDefault) need_infill &= area_group.need_infill; - std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern)); - filler_support->set_bounding_box(bbox_object); - filler_support->spacing = support_flow.spacing(); + std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern)); + filler_support->set_bounding_box(bbox_object); + filler_support->spacing = support_flow.spacing(); filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); Polygons loops = to_polygons(poly); @@ -1786,8 +1795,10 @@ void TreeSupport::generate() profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); //m_object->print()->set_status(56, _u8L("Support: precalculate avoidance")); - Points bedpts = m_machine_border.contour.points; - BuildVolume build_volume{ Pointfs{ unscaled(bedpts[0]), unscaled(bedpts[1]),unscaled(bedpts[2]),unscaled(bedpts[3])}, m_print_config->printable_height }; + Points bedpts = m_machine_border.contour.points; + Pointfs bedptsf; + std::transform(bedpts.begin(), bedpts.end(), std::back_inserter(bedptsf), [](const Point &p) { return unscale(p); }); + BuildVolume build_volume{bedptsf, m_print_config->printable_height, {}}; TreeSupport3D::TreeSupportSettings tree_support_3d_config{ TreeSupport3D::TreeSupportMeshGroupSettings{ *m_object }, m_slicing_params }; m_model_volumes = std::make_unique( *m_object, build_volume, tree_support_3d_config.maximum_move_distance, tree_support_3d_config.maximum_move_distance_slow, 1); // ### Precalculate avoidances, collision etc. @@ -2101,12 +2112,11 @@ void TreeSupport::draw_circles() } // generate areas - const coordf_t layer_height = config.layer_height.value; const size_t top_interface_layers = config.support_interface_top_layers.value; const size_t bottom_interface_layers = config.support_interface_bottom_layers.value; const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning; coordf_t support_extrusion_width = m_support_params.support_extrusion_width; - const coordf_t line_width_scaled = scale_(support_extrusion_width); + const coordf_t line_width_scaled = scale_(support_extrusion_width); const float tree_brim_width = config.raft_first_layer_expansion.value; if (m_object->support_layer_count() <= m_raft_layers) @@ -2243,7 +2253,7 @@ void TreeSupport::draw_circles() } } - if (layer_nr == 0 && m_raft_layers == 0) + if (layer_nr == 0 && m_raft_layers == 0) area = safe_offset_inc(area, scale_(brim_width), get_collision(false), scale_(MIN_BRANCH_RADIUS * 0.5), 0, 1); if (obj_layer_nr>0 && node.distance_to_top < 0) append(roof_gap_areas, area); @@ -2365,8 +2375,6 @@ void TreeSupport::draw_circles() if (with_lightning_infill) { - const bool global_lightning_infill = true; - std::vector contours; std::vector overhangs; for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { @@ -2391,33 +2399,25 @@ void TreeSupport::draw_circles() SupportLayer* lower_layer = m_object->get_support_layer(layer_nr_lower + m_raft_layers); ExPolygons& base_areas_lower = lower_layer->base_areas; - ExPolygons overhang; - if (global_lightning_infill) - { - //search overhangs globally - overhang = std::move(diff_ex(offset_ex(base_areas_lower, -2.0 * scale_(support_extrusion_width)), base_areas)); - } - else - { - //search overhangs only on floating islands - for (auto& base_area : base_areas) - for (auto& hole : base_area.holes) - { - Polygon rev_hole = hole; - rev_hole.make_counter_clockwise(); - ExPolygons ex_hole; - ex_hole.emplace_back(std::move(ExPolygon(rev_hole))); - for (auto& other_area : base_areas) - //if (&other_area != &base_area) - ex_hole = std::move(diff_ex(ex_hole, other_area)); - overhang = std::move(union_ex(overhang, ex_hole)); - } - overhang = std::move(intersection_ex(overhang, offset_ex(base_areas_lower, -0.5 * scale_(support_extrusion_width)))); + ExPolygons overhang = diff_ex(offset_ex_2(base_areas_lower, -2.5 * line_width_scaled), base_areas); + for (auto it = overhang.begin(); it != overhang.end();) { + it->remove_colinear_points(); + if (it->empty()) + it = overhang.erase(it); + else ++it; } + overhangs.emplace_back(to_polygons(overhang)); contours.emplace_back(to_polygons(base_areas_lower)); printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1; + +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + if (!overhang.empty() && !base_areas_lower.empty()) { + std::string fname = debug_out_path("lightning_%d_%.2f.svg", layer_nr, ts_layer->print_z); + SVG::export_expolygons(fname, {{base_areas_lower, {"base_areas_lower", "red", 0.5}}, {overhang, {"overhang", "blue", 0.5}}}); + } +#endif } @@ -3369,11 +3369,11 @@ void TreeSupport::smooth_nodes() int idx_last_double_wall = -1; for (size_t i = 0; i < pts.size(); i++) { if (branch[i]->need_extra_wall) { - if (idx_first_double_wall < 0) idx_first_double_wall = i; + if (idx_first_double_wall < 0) idx_first_double_wall = i; idx_last_double_wall = i; } } - if (idx_last_double_wall >= 0 && idx_first_double_wall >= 0 && idx_last_double_wall > idx_first_double_wall) { + if (idx_last_double_wall >= 0 && idx_first_double_wall >= 0 && idx_last_double_wall > idx_first_double_wall) { for (size_t i = idx_first_double_wall + 1; i < idx_last_double_wall; i++) branch[i]->need_extra_wall = true; } } @@ -3910,14 +3910,14 @@ TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance ExPolygons &outline = m_layer_outlines[layer_nr]; outline.clear(); outline.reserve(layer->lslices.size()); - for (const ExPolygon &poly : layer->lslices) { append(outline, union_ex2( poly.simplify_p(scale_(m_radius_sample_resolution)))); } + for (const ExPolygon &poly : layer->lslices) { append(outline, union_ex_2( poly.simplify_p(scale_(m_radius_sample_resolution)))); } if (layer_nr == 0) m_layer_outlines_below[layer_nr] = outline; else { m_layer_outlines_below[layer_nr] = m_layer_outlines_below[layer_nr - 1]; m_layer_outlines_below[layer_nr].insert(m_layer_outlines_below[layer_nr].end(), outline.begin(), outline.end()); - if (layer_nr % 10 == 0) m_layer_outlines_below[layer_nr] = union_ex2(m_layer_outlines_below[layer_nr]); + if (layer_nr % 10 == 0) m_layer_outlines_below[layer_nr] = union_ex_2(m_layer_outlines_below[layer_nr]); } } } @@ -4032,6 +4032,10 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke avoidance_areas = std::move(union_ex(avoidance_areas)); auto ret = m_avoidance_cache.insert({key, std::move(avoidance_areas)}); //assert(ret.second); + // BOOST_LOG_TRIVIAL(debug) << format("calculate_avoidance: radius=%.2f, layer_nr=%d, recursions=%d, avoidance_areas=%d", radius, layer_nr, key.recursions, + // avoidance_areas.size()); + // boost::log::core::get()->flush(); + return ret.first->second; }