From bbb7e839a056f51e1218eb966f3acd9122a9022f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 16 Mar 2022 10:57:18 +0100 Subject: [PATCH 01/14] Fixed various issues in the ported Lightning infill. Added export to SVG for TreeNode. Also was finalized integration of Lightning infill and Lightning infill was enabled. --- src/libslic3r/Fill/Fill.cpp | 6 +- src/libslic3r/Fill/FillBase.cpp | 2 - src/libslic3r/Fill/FillRectilinear.cpp | 8 +- .../Fill/Lightning/DistanceField.cpp | 67 ++++++++++++---- .../Fill/Lightning/DistanceField.hpp | 26 ++++++- src/libslic3r/Fill/Lightning/Generator.cpp | 42 +++++----- src/libslic3r/Fill/Lightning/Generator.hpp | 2 +- src/libslic3r/Fill/Lightning/Layer.cpp | 77 ++++++++++++------- src/libslic3r/Fill/Lightning/Layer.hpp | 19 +++-- src/libslic3r/Fill/Lightning/TreeNode.cpp | 50 ++++++++---- src/libslic3r/Fill/Lightning/TreeNode.hpp | 33 +++++--- src/libslic3r/Layer.hpp | 8 +- src/libslic3r/Point.hpp | 5 ++ src/libslic3r/Print.hpp | 9 ++- src/libslic3r/PrintConfig.cpp | 6 -- src/libslic3r/PrintConfig.hpp | 8 +- src/libslic3r/PrintObject.cpp | 18 ++++- 17 files changed, 265 insertions(+), 121 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index a3e4aee31..e6b820188 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -11,6 +11,7 @@ #include "FillBase.hpp" #include "FillRectilinear.hpp" +#include "FillLightning.hpp" namespace Slic3r { @@ -318,7 +319,7 @@ void export_group_fills_to_svg(const char *path, const std::vector #endif // friend to Layer -void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree) +void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) { for (LayerRegion *layerm : m_regions) layerm->fills.clear(); @@ -348,6 +349,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->angle = surface_fill.params.angle; f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; + if (surface_fill.params.pattern == ipLightning) + dynamic_cast(f.get())->generator = lightning_generator; + // calculate flow spacing for infill pattern generation bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge; double link_max_length = 0.; diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 7ca0699c0..c4f8d6f08 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -46,9 +46,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipAdaptiveCubic: return new FillAdaptive::Filler(); case ipSupportCubic: return new FillAdaptive::Filler(); case ipSupportBase: return new FillSupportBase(); -#if HAS_LIGHTNING_INFILL case ipLightning: return new FillLightning::Filler(); -#endif // HAS_LIGHTNING_INFILL default: throw Slic3r::InvalidArgument("unknown type"); } } diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 69476175e..bcff05197 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -406,13 +406,15 @@ public: // for the infill pattern, don't cut the corners. // default miterLimt = 3 //double miterLimit = 10.; - assert(aoffset1 < 0); + // FIXME: Resolve properly the cases when it is constructed with aoffset1 = 0 and aoffset2 = 0, + // that is used in sample_grid_pattern() for Lightning infill. + // assert(aoffset1 < 0); assert(aoffset2 <= 0); - assert(aoffset2 == 0 || aoffset2 < aoffset1); + // assert(aoffset2 == 0 || aoffset2 < aoffset1); // bool sticks_removed = remove_sticks(polygons_src); // if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!"; - polygons_outer = offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); + polygons_outer = aoffset1 == 0 ? polygons_src : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); if (aoffset2 < 0) polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit); // Filter out contours with zero area or small area, contours with 2 points only. diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index 6df1d043c..308ca41c6 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -10,27 +10,29 @@ namespace Slic3r::FillLightning constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient. -DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) : - m_cell_size(radius / radius_per_cell_size), - m_supporting_radius(radius) +DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const BoundingBox& current_outlines_bbox, const Polygons& current_overhang) : + m_cell_size(radius / radius_per_cell_size), + m_supporting_radius(radius), + m_unsupported_points_bbox(current_outlines_bbox) { - m_supporting_radius2 = double(radius) * double(radius); + m_supporting_radius2 = Slic3r::sqr(int64_t(radius)); // Sample source polygons with a regular grid sampling pattern. - for (const ExPolygon &expoly : union_ex(current_outline)) { + for (const ExPolygon &expoly : union_ex(current_overhang)) { for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) { // Find a squared distance to the source expolygon boundary. double d2 = std::numeric_limits::max(); - for (size_t icontour = 0; icontour <= expoly.holes.size(); ++ icontour) { + for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) { const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1]; if (contour.size() > 2) { Point prev = contour.points.back(); for (const Point &p2 : contour.points) { - d2 = std::min(d2, Line::distance_to_squared(p, prev, p2)); + d2 = std::min(d2, Line::distance_to_squared(p, prev, p2)); prev = p2; } } } m_unsupported_points.emplace_back(p, sqrt(d2)); + assert(m_unsupported_points_bbox.contains(p)); } } m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) { @@ -41,8 +43,11 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl }); for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) { UnsupportedCell& cell = *it; - m_unsupported_points_grid.emplace(Point{ cell.loc.x() / m_cell_size, cell.loc.y() / m_cell_size }, it); + m_unsupported_points_grid.emplace(this->to_grid_point(cell.loc), it); } + // Because the distance between two points is at least one axis equal to m_cell_size, every cell + // in m_unsupported_points_grid contains exactly one point. + assert(m_unsupported_points.size() == m_unsupported_points_grid.size()); } void DistanceField::update(const Point& to_node, const Point& added_leaf) @@ -60,17 +65,24 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf) grid.merge(to_node + iextent); grid.merge(added_leaf - iextent); grid.merge(added_leaf + iextent); - grid.min /= m_cell_size; - grid.max /= m_cell_size; + + // Clip grid by m_unsupported_points_bbox. Mainly to ensure that grid.min is a non-negative value. + grid.min.x() = std::max(grid.min.x(), m_unsupported_points_bbox.min.x()); + grid.min.y() = std::max(grid.min.y(), m_unsupported_points_bbox.min.y()); + grid.max.x() = std::min(grid.max.x(), m_unsupported_points_bbox.max.x()); + grid.max.y() = std::min(grid.max.y(), m_unsupported_points_bbox.max.y()); + + grid.min = this->to_grid_point(grid.min); + grid.max = this->to_grid_point(grid.max); } + Point grid_addr; Point grid_loc; - for (coord_t row = grid.min.y(); row <= grid.max.y(); ++ row) { - grid_loc.y() = row * m_cell_size; - for (coord_t col = grid.min.x(); col <= grid.max.y(); ++ col) { - grid_loc.x() = col * m_cell_size; + for (grid_addr.y() = grid.min.y(); grid_addr.y() <= grid.max.y(); ++grid_addr.y()) { + for (grid_addr.x() = grid.min.x(); grid_addr.x() <= grid.max.x(); ++grid_addr.x()) { + grid_loc = this->from_grid_point(grid_addr); // Test inside a circle at the new leaf. - if ((grid_loc - added_leaf).cast().squaredNorm() > m_supporting_radius2) { + if ((grid_loc - added_leaf).cast().squaredNorm() > m_supporting_radius2) { // Not inside a circle at the end of the new leaf. // Test inside a rotated rectangle. Vec2d vx = (grid_loc - to_node).cast(); @@ -84,10 +96,10 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf) } // Inside a circle at the end of the new leaf, or inside a rotated rectangle. // Remove unsupported leafs at this grid location. - if (auto it = m_unsupported_points_grid.find(grid_loc); it != m_unsupported_points_grid.end()) { + if (auto it = m_unsupported_points_grid.find(grid_addr); it != m_unsupported_points_grid.end()) { std::list::iterator& list_it = it->second; UnsupportedCell& cell = *list_it; - if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { + if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { m_unsupported_points.erase(list_it); m_unsupported_points_grid.erase(it); } @@ -96,4 +108,25 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf) } } +#if 0 +void DistanceField::update(const Point &to_node, const Point &added_leaf) +{ + const Point supporting_radius_point(m_supporting_radius, m_supporting_radius); + const BoundingBox grid(this->to_grid_point(added_leaf - supporting_radius_point), this->to_grid_point(added_leaf + supporting_radius_point)); + + for (coord_t grid_y = grid.min.y(); grid_y <= grid.max.y(); ++grid_y) { + for (coord_t grid_x = grid.min.x(); grid_x <= grid.max.x(); ++grid_x) { + if (auto it = m_unsupported_points_grid.find({grid_x, grid_y}); it != m_unsupported_points_grid.end()) { + std::list::iterator &list_it = it->second; + UnsupportedCell &cell = *list_it; + if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { + m_unsupported_points.erase(list_it); + m_unsupported_points_grid.erase(it); + } + } + } + } +} +#endif + } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index 7fdce434c..beb46c5c5 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -4,6 +4,7 @@ #ifndef LIGHTNING_DISTANCE_FIELD_H #define LIGHTNING_DISTANCE_FIELD_H +#include "../../BoundingBox.hpp" #include "../../Point.hpp" #include "../../Polygon.hpp" @@ -29,7 +30,7 @@ public: * \param current_overhang The overhang that needs to be supported on this * layer. */ - DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang); + DistanceField(const coord_t& radius, const Polygons& current_outline, const BoundingBox& current_outlines_bbox, const Polygons& current_overhang); /*! * Gets the next unsupported location to be supported by a new branch. @@ -69,14 +70,14 @@ protected: * branch of a tree. */ coord_t m_supporting_radius; - double m_supporting_radius2; + int64_t m_supporting_radius2; /*! * Represents a small discrete area of infill that needs to be supported. */ struct UnsupportedCell { - UnsupportedCell(Point loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} + UnsupportedCell(const Point &loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} // The position of the center of this cell. Point loc; // How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area. @@ -88,11 +89,30 @@ protected: */ std::list m_unsupported_points; + /*! + * BoundingBox of all points in m_unsupported_points. Used for mapping of sign integer numbers to positive integer numbers. + */ + const BoundingBox m_unsupported_points_bbox; + /*! * Links the unsupported points to a grid point, so that we can quickly look * up the cell belonging to a certain position in the grid. */ std::unordered_map::iterator, PointHash> m_unsupported_points_grid; + + /*! + * Maps the point to the grid coordinates. + */ + Point to_grid_point(const Point &point) const { + return (point - m_unsupported_points_bbox.min) / m_cell_size; + } + + /*! + * Maps the point to the grid coordinates. + */ + Point from_grid_point(const Point &point) const { + return point * m_cell_size + m_unsupported_points_bbox.min; + } }; } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index 87b593f89..6a4a675cb 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -35,17 +35,17 @@ Generator::Generator(const PrintObject &print_object) // const int infill_extruder = region_config.infill_extruder.value; const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); // Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account. - const double layer_thickness = object_config.layer_height; + const double layer_thickness = scaled(object_config.layer_height.value); m_infill_extrusion_width = scaled(region_config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * region_config.infill_extrusion_width : region_config.infill_extrusion_width); - m_supporting_radius = scaled(m_infill_extrusion_width * 0.001 / region_config.fill_density); + m_supporting_radius = coord_t(m_infill_extrusion_width) * 100 / coord_t(region_config.fill_density.value); - const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees - const double lightning_infill_prune_angle = M_PI / 4; // 45 degrees + const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees + const double lightning_infill_prune_angle = M_PI / 4; // 45 degrees const double lightning_infill_straightening_angle = M_PI / 4; // 45 degrees - m_wall_supporting_radius = layer_thickness * std::tan(lightning_infill_overhang_angle); - m_prune_length = layer_thickness * std::tan(lightning_infill_prune_angle); - m_straightening_max_distance = layer_thickness * std::tan(lightning_infill_straightening_angle); + m_wall_supporting_radius = coord_t(layer_thickness * std::tan(lightning_infill_overhang_angle)); + m_prune_length = coord_t(layer_thickness * std::tan(lightning_infill_prune_angle)); + m_straightening_max_distance = coord_t(layer_thickness * std::tan(lightning_infill_straightening_angle)); generateInitialInternalOverhangs(print_object); generateTrees(print_object); @@ -54,19 +54,20 @@ Generator::Generator(const PrintObject &print_object) void Generator::generateInitialInternalOverhangs(const PrintObject &print_object) { m_overhang_per_layer.resize(print_object.layers().size()); - const float infill_wall_offset = - m_infill_extrusion_width; + // FIXME: It can be adjusted to improve bonding between infill and perimeters. + const float infill_wall_offset = 0;// m_infill_extrusion_width; Polygons infill_area_above; //Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging. - for (int layer_nr = print_object.layers().size() - 1; layer_nr >= 0; layer_nr--) { + for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; layer_nr--) { Polygons infill_area_here; for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions()) for (const Surface& surface : layerm->fill_surfaces.surfaces) if (surface.surface_type == stInternal) - append(infill_area_here, offset(surface.expolygon, infill_wall_offset)); + append(infill_area_here, infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset)); //Remove the part of the infill area that is already supported by the walls. - Polygons overhang = diff(offset(infill_area_here, -m_wall_supporting_radius), infill_area_above); + Polygons overhang = diff(offset(infill_area_here, -float(m_wall_supporting_radius)), infill_area_above); m_overhang_per_layer[layer_nr] = overhang; infill_area_above = std::move(infill_area_here); @@ -82,16 +83,17 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const void Generator::generateTrees(const PrintObject &print_object) { m_lightning_layers.resize(print_object.layers().size()); - const coord_t infill_wall_offset = - m_infill_extrusion_width; + // FIXME: It can be adjusted to improve bonding between infill and perimeters. + const coord_t infill_wall_offset = 0;// m_infill_extrusion_width; std::vector infill_outlines(print_object.layers().size(), Polygons()); // For-each layer from top to bottom: - for (int layer_id = print_object.layers().size() - 1; layer_id >= 0; layer_id--) + for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions()) for (const Surface &surface : layerm->fill_surfaces.surfaces) if (surface.surface_type == stInternal) - append(infill_outlines[layer_id], offset(surface.expolygon, infill_wall_offset)); + append(infill_outlines[layer_id], infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset)); // For various operations its beneficial to quickly locate nearby features on the polygon: const size_t top_layer_id = print_object.layers().size() - 1; @@ -99,16 +101,16 @@ void Generator::generateTrees(const PrintObject &print_object) outlines_locator.create(infill_outlines[top_layer_id], locator_cell_size); // For-each layer from top to bottom: - for (int layer_id = top_layer_id; layer_id >= 0; layer_id--) - { - Layer& current_lightning_layer = m_lightning_layers[layer_id]; - Polygons& current_outlines = infill_outlines[layer_id]; + for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) { + Layer ¤t_lightning_layer = m_lightning_layers[layer_id]; + const Polygons ¤t_outlines = infill_outlines[layer_id]; + const BoundingBox ¤t_outlines_bbox = get_extents(current_outlines); // register all trees propagated from the previous layer as to-be-reconnected std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; - current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); - current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); // Initialize trees for next lower layer from the current one. if (layer_id == 0) diff --git a/src/libslic3r/Fill/Lightning/Generator.hpp b/src/libslic3r/Fill/Lightning/Generator.hpp index c44ecfe7a..b538c4dbe 100644 --- a/src/libslic3r/Fill/Lightning/Generator.hpp +++ b/src/libslic3r/Fill/Lightning/Generator.hpp @@ -45,7 +45,7 @@ public: * already be calculated at this point. * \param mesh The mesh to generate infill for. */ - Generator(const PrintObject &print_object); + explicit Generator(const PrintObject &print_object); /*! * Get a tree of paths generated for a certain layer of the mesh. diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index 1b3936e34..1e1127a79 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -3,12 +3,11 @@ #include "Layer.hpp" //The class we're implementing. -#include // advance - #include "DistanceField.hpp" #include "TreeNode.hpp" #include "../../Geometry.hpp" +#include "Utils.hpp" namespace Slic3r::FillLightning { @@ -23,10 +22,15 @@ Point GroundingLocation::p() const return tree_node ? tree_node->getLocation() : *boundary_location; } -void Layer::fillLocator(SparseNodeGrid &tree_node_locator) +inline static Point to_grid_point(const Point &point, const BoundingBox &bbox) { - std::function add_node_to_locator_func = [&tree_node_locator](NodeSPtr node) { - tree_node_locator.insert(std::make_pair(Point(node->getLocation().x() / locator_cell_size, node->getLocation().y() / locator_cell_size), node)); + return (point - bbox.min) / locator_cell_size; +} + +void Layer::fillLocator(SparseNodeGrid &tree_node_locator, const BoundingBox& current_outlines_bbox) +{ + std::function add_node_to_locator_func = [&tree_node_locator, ¤t_outlines_bbox](const NodeSPtr &node) { + tree_node_locator.insert(std::make_pair(to_grid_point(node->getLocation(), current_outlines_bbox), node)); }; for (auto& tree : tree_roots) tree->visitNodes(add_node_to_locator_func); @@ -36,38 +40,46 @@ void Layer::generateNewTrees ( const Polygons& current_overhang, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outlines_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius ) { - DistanceField distance_field(supporting_radius, current_outlines, current_overhang); + DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang); SparseNodeGrid tree_node_locator; - fillLocator(tree_node_locator); + fillLocator(tree_node_locator, current_outlines_bbox); // Until no more points need to be added to support all: // Determine next point from tree/outline areas via distance-field Point unsupported_location; while (distance_field.tryGetNextPoint(&unsupported_location)) { GroundingLocation grounding_loc = getBestGroundingLocation( - unsupported_location, current_outlines, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); + unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); NodeSPtr new_parent; NodeSPtr new_child; this->attach(unsupported_location, grounding_loc, new_child, new_parent); - tree_node_locator.insert(std::make_pair(Point(new_child->getLocation().x() / locator_cell_size, new_child->getLocation().y() / locator_cell_size), new_child)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_child->getLocation(), current_outlines_bbox), new_child)); if (new_parent) - tree_node_locator.insert(std::make_pair(Point(new_parent->getLocation().x() / locator_cell_size, new_parent->getLocation().y() / locator_cell_size), new_parent)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_parent->getLocation(), current_outlines_bbox), new_parent)); // update distance field distance_field.update(grounding_loc.p(), unsupported_location); } + +#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT + { + static int iRun = 0; + export_to_svg(debug_out_path("FillLightning-TreeNodes-%d.svg", iRun++), current_outlines, this->tree_roots); + } +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ } -static bool polygonCollidesWithLineSegment(const Point from, const Point to, const EdgeGrid::Grid &loc_to_line) +static bool polygonCollidesWithLineSegment(const Point &from, const Point &to, const EdgeGrid::Grid &loc_to_line) { struct Visitor { - explicit Visitor(const EdgeGrid::Grid &grid) : grid(grid) {} + explicit Visitor(const EdgeGrid::Grid &grid, const Line &line) : grid(grid), line(line) {} bool operator()(coord_t iy, coord_t ix) { // Called with a row and colum of the grid cell, which is intersected by a line. @@ -87,7 +99,7 @@ static bool polygonCollidesWithLineSegment(const Point from, const Point to, con const EdgeGrid::Grid& grid; Line line; bool intersect = false; - } visitor(loc_to_line); + } visitor(loc_to_line, {from, to}); loc_to_line.visit_cells_intersecting_line(from, to, visitor); return visitor.intersect; @@ -97,6 +109,7 @@ GroundingLocation Layer::getBestGroundingLocation ( const Point& unsupported_location, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius, @@ -128,8 +141,8 @@ GroundingLocation Layer::getBestGroundingLocation if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines. const coord_t search_radius = std::min(current_dist, within_dist); BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size)); - region.min /= locator_cell_size; - region.max /= locator_cell_size; + region.min = to_grid_point(region.min, current_outlines_bbox); + region.max = to_grid_point(region.max, current_outlines_bbox); Point grid_addr; for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y()) for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) { @@ -176,6 +189,7 @@ void Layer::reconnectRoots ( std::vector& to_be_reconnected_tree_roots, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius @@ -184,10 +198,10 @@ void Layer::reconnectRoots constexpr coord_t tree_connecting_ignore_offset = 100; SparseNodeGrid tree_node_locator; - fillLocator(tree_node_locator); + fillLocator(tree_node_locator, current_outlines_bbox); const coord_t within_max_dist = outline_locator.resolution() * 2; - for (auto root_ptr : to_be_reconnected_tree_roots) + for (const auto &root_ptr : to_be_reconnected_tree_roots) { auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr); @@ -203,7 +217,7 @@ void Layer::reconnectRoots root_ptr->addChild(new_root); new_root->reroot(); - tree_node_locator.insert(std::make_pair(Point(new_root->getLocation().x() / locator_cell_size, new_root->getLocation().y() / locator_cell_size), new_root)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root)); *old_root_it = std::move(new_root); // replace old root with new root continue; @@ -217,6 +231,7 @@ void Layer::reconnectRoots ( root_ptr->getLocation(), current_outlines, + current_outlines_bbox, outline_locator, supporting_radius, tree_connecting_ignore_width, @@ -233,7 +248,7 @@ void Layer::reconnectRoots attach_ptr->reroot(); new_root->addChild(attach_ptr); - tree_node_locator.insert(std::make_pair(new_root->getLocation(), new_root)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root)); *old_root_it = std::move(new_root); // replace old root with new root } @@ -256,15 +271,25 @@ void Layer::reconnectRoots } } -/* - * Implementation assumes moving inside, but moving outside should just as well be possible. +/*! + * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance. + * Given a \p distance more than zero, the point will end up inside, and conversely outside. + * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. + * When the point is in/outside by less than \p distance, \p from is moved to the correct place. + * Implementation assumes moving inside, but moving outside should just as well be possible. + * + * \param polygons The polygons onto which to move the point + * \param from[in,out] The point to move. + * \param distance The distance by which to move the point. + * \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon. + * \return The index to the polygon onto which we have moved the point. */ static unsigned int moveInside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2) { - Point ret = from; - int64_t bestDist2 = std::numeric_limits::max(); - unsigned int bestPoly = static_cast(-1); - bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary + Point ret = from; + int64_t bestDist2 = std::numeric_limits::max(); + auto bestPoly = static_cast(-1); + bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) { const Polygon &poly = polygons[poly_idx]; @@ -333,7 +358,7 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan else { // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | . projected_p_beyond_prev_segment = false; - Point x = a + ab * dot_prod / ab_length2; + Point x = (a.cast() + ab.cast() * dot_prod / ab_length2).cast(); int64_t dist2 = (p - x).cast().squaredNorm(); if (dist2 < bestDist2) diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp index 853c634f2..cc79c15b5 100644 --- a/src/libslic3r/Fill/Lightning/Layer.hpp +++ b/src/libslic3r/Fill/Lightning/Layer.hpp @@ -41,9 +41,10 @@ public: ( const Polygons& current_overhang, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, - const coord_t supporting_radius, - const coord_t wall_supporting_radius + coord_t supporting_radius, + coord_t wall_supporting_radius ); /*! Determine & connect to connection point in tree/outline. @@ -53,9 +54,10 @@ public: ( const Point& unsupported_location, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, - const coord_t supporting_radius, - const coord_t wall_supporting_radius, + coord_t supporting_radius, + coord_t wall_supporting_radius, const SparseNodeGrid& tree_node_locator, const NodeSPtr& exclude_tree = nullptr ); @@ -71,16 +73,17 @@ public: ( std::vector& to_be_reconnected_tree_roots, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, - const coord_t supporting_radius, - const coord_t wall_supporting_radius + coord_t supporting_radius, + coord_t wall_supporting_radius ); - Polylines convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const; + Polylines convertToLines(const Polygons& limit_to_outline, coord_t line_width) const; coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location); - void fillLocator(SparseNodeGrid& tree_node_locator); + void fillLocator(SparseNodeGrid& tree_node_locator, const BoundingBox& current_outlines_bbox); }; } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index 8ab11bd6d..b36a66dbb 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -4,7 +4,6 @@ #include "TreeNode.hpp" #include "../../Geometry.hpp" -#include "../../ClipperUtils.hpp" namespace Slic3r::FillLightning { @@ -107,7 +106,7 @@ NodeSPtr Node::deepCopy() const return local_root; } -void Node::reroot(NodeSPtr new_parent /*= nullptr*/) +void Node::reroot(const NodeSPtr &new_parent) { if (! m_is_root) { auto old_parent = m_parent.lock(); @@ -142,7 +141,7 @@ NodeSPtr Node::closestNode(const Point& loc) return result; } -bool inside(const Polygons &polygons, const Point p) +bool inside(const Polygons &polygons, const Point &p) { int poly_count_inside = 0; for (const Polygon &poly : polygons) { @@ -181,7 +180,7 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG } visitor { outline_locator, a.cast(), b.cast() }; outline_locator.visit_cells_intersecting_line(a, b, visitor); - return visitor.d2min < within_max_dist * within_max_dist; + return visitor.d2min < double(within_max_dist) * double(within_max_dist); } bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts) @@ -226,14 +225,14 @@ bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locat void Node::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist) { - straighten(magnitude, m_p, 0, max_remove_colinear_dist * max_remove_colinear_dist); + straighten(magnitude, m_p, 0, int64_t(max_remove_colinear_dist) * int64_t(max_remove_colinear_dist)); } Node::RectilinearJunction Node::straighten( const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, - const coord_t max_remove_colinear_dist2) + const int64_t max_remove_colinear_dist2) { constexpr coord_t junction_magnitude_factor_numerator = 3; constexpr coord_t junction_magnitude_factor_denominator = 4; @@ -245,13 +244,13 @@ Node::RectilinearJunction Node::straighten( auto child_dist = coord_t((m_p - child_p->m_p).cast().norm()); RectilinearJunction junction_below = child_p->straighten(magnitude, junction_above, accumulated_dist + child_dist, max_remove_colinear_dist2); coord_t total_dist_to_junction_below = junction_below.total_recti_dist; - Point a = junction_above; - Point b = junction_below.junction_loc; + const Point& a = junction_above; + Point b = junction_below.junction_loc; if (a != b) // should always be true! { Point ab = b - a; - Point destination = a + ab * accumulated_dist / std::max(coord_t(1), total_dist_to_junction_below); - if ((destination - m_p).cast().squaredNorm() <= magnitude * magnitude) + Point destination = (a.cast() + ab.cast() * int64_t(accumulated_dist) / std::max(int64_t(1), int64_t(total_dist_to_junction_below))).cast(); + if ((destination - m_p).cast().squaredNorm() <= int64_t(magnitude) * int64_t(magnitude)) m_p = destination; else m_p += ((destination - m_p).cast().normalized() * magnitude).cast(); @@ -262,7 +261,7 @@ Node::RectilinearJunction Node::straighten( child_p = m_children.front(); //recursive call to straighten might have removed the child const NodeSPtr& parent_node = m_parent.lock(); if (parent_node && - (child_p->m_p - parent_node->m_p).cast().squaredNorm() < max_remove_colinear_dist2 && + (child_p->m_p - parent_node->m_p).cast().squaredNorm() < max_remove_colinear_dist2 && Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) { child_p->m_parent = m_parent; for (auto& sibling : parent_node->m_children) @@ -347,7 +346,7 @@ coord_t Node::prune(const coord_t& pruning_distance) void Node::convertToPolylines(Polygons& output, const coord_t line_width) const { Polygons result; - output.emplace_back(); + result.emplace_back(); convertToPolylines(0, result); removeJunctionOverlap(result, line_width); append(output, std::move(result)); @@ -386,7 +385,7 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt coord_t to_be_reduced = reduction; Point a = polyline.back(); - for (int point_idx = polyline.size() - 2; point_idx >= 0; point_idx--) { + for (int point_idx = int(polyline.size()) - 2; point_idx >= 0; point_idx--) { const Point b = polyline[point_idx]; const Point ab = b - a; const auto ab_len = coord_t(ab.cast().norm()); @@ -408,4 +407,29 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt } } +#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT +void export_to_svg(const NodeSPtr &root_node, SVG &svg) +{ + for (const NodeSPtr &children : root_node->m_children) { + svg.draw(Line(root_node->getLocation(), children->getLocation()), "red"); + export_to_svg(children, svg); + } +} + +void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes) { + BoundingBox bbox = get_extents(contour); + + bbox.offset(SCALED_EPSILON); + SVG svg(path, bbox); + svg.draw_outline(contour, "blue"); + + for (const NodeSPtr &root_node: root_nodes) { + for (const NodeSPtr &children: root_node->m_children) { + svg.draw(Line(root_node->getLocation(), children->getLocation()), "red"); + export_to_svg(children, svg); + } + } +} +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ + } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp index bc2e76dc2..55ef35e0a 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.hpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp @@ -11,6 +11,9 @@ #include "../../EdgeGrid.hpp" #include "../../Polygon.hpp" +#include "SVG.hpp" + +//#define LIGHTNING_TREE_NODE_DEBUG_OUTPUT namespace Slic3r::FillLightning { @@ -99,9 +102,9 @@ public: std::vector& next_trees, const Polygons& next_outlines, const EdgeGrid::Grid& outline_locator, - const coord_t prune_distance, - const coord_t smooth_magnitude, - const coord_t max_remove_colinear_dist + coord_t prune_distance, + coord_t smooth_magnitude, + coord_t max_remove_colinear_dist ) const; /*! @@ -156,7 +159,7 @@ public: * This is then recursively bubbled up until it reaches the (former) root, which then will become a leaf. * \param new_parent The (new) parent-node of the root, useful for recursing or immediately attaching the node to another tree. */ - void reroot(NodeSPtr new_parent = nullptr); + void reroot(const NodeSPtr &new_parent = nullptr); /*! * Retrieves the closest node to the specified location. @@ -211,7 +214,7 @@ protected: * \param magnitude The maximum allowed distance to move the node. * \param max_remove_colinear_dist Maximum distance of the (compound) line-segment from which a co-linear point may be removed. */ - void straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist); + void straighten(coord_t magnitude, coord_t max_remove_colinear_dist); /*! Recursive part of \ref straighten(.) * \param junction_above The last seen junction with multiple children above @@ -219,7 +222,7 @@ protected: * \param max_remove_colinear_dist2 Maximum distance _squared_ of the (compound) line-segment from which a co-linear point may be removed. * \return the total distance along the tree from the last junction above to the first next junction below and the location of the next junction below */ - RectilinearJunction straighten(const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, const coord_t max_remove_colinear_dist2); + RectilinearJunction straighten(coord_t magnitude, const Point& junction_above, coord_t accumulated_dist, int64_t max_remove_colinear_dist2); /*! Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached. * \return The distance that has been pruned. If less than \p distance, then the whole tree was puned away. @@ -236,7 +239,7 @@ public: * * \param output all branches in this tree connected into polylines */ - void convertToPolylines(Polygons& output, const coord_t line_width) const; + void convertToPolylines(Polygons& output, coord_t line_width) const; /*! If this was ever a direct child of the root, it'll have a previous grounding location. * @@ -257,7 +260,7 @@ protected: */ void convertToPolylines(size_t long_line_idx, Polygons& output) const; - void removeJunctionOverlap(Polygons& polylines, const coord_t line_width) const; + void removeJunctionOverlap(Polygons& polylines, coord_t line_width) const; bool m_is_root; Point m_p; @@ -265,10 +268,20 @@ protected: std::vector m_children; std::optional m_last_grounding_location; // &root_nodes); +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ }; -bool inside(const Polygons &polygons, const Point p); -bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist); +bool inside(const Polygons &polygons, const Point &p); +bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, coord_t within_max_dist); + +#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT +void export_to_svg(const NodeSPtr &root_node, SVG &svg); +void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes); +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 0071c7f6e..a308ac0b6 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -20,6 +20,10 @@ namespace FillAdaptive { struct Octree; }; +namespace FillLightning { + class Generator; +}; + class LayerRegion { public: @@ -151,8 +155,8 @@ public: } void make_perimeters(); // Phony version of make_fills() without parameters for Perl integration only. - void make_fills() { this->make_fills(nullptr, nullptr); } - void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree); + void make_fills() { this->make_fills(nullptr, nullptr, nullptr); } + void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator); void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index e5dc334f5..6b430c2fe 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -177,6 +177,11 @@ inline bool operator<(const Point &l, const Point &r) return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y()); } +inline Point operator* (const Point& l, const double &r) +{ + return {coord_t(l.x() * r), coord_t(l.y() * r)}; +} + inline bool is_approx(const Point &p1, const Point &p2, coord_t epsilon = coord_t(SCALED_EPSILON)) { Point d = (p2 - p1).cwiseAbs(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c2babd53c..61151babd 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -35,7 +35,13 @@ namespace FillAdaptive { struct Octree; struct OctreeDeleter; using OctreePtr = std::unique_ptr; -}; +}; // namespace FillAdaptive + +namespace FillLightning { + class Generator; + struct GeneratorDeleter; + using GeneratorPtr = std::unique_ptr; +}; // namespace FillLightning // Print step IDs for keeping track of the print state. // The Print steps are applied in this order. @@ -382,6 +388,7 @@ private: void combine_infill(); void _generate_support_material(); std::pair prepare_adaptive_infill_data(); + FillLightning::GeneratorPtr prepare_lightning_infill_data(); // XYZ in scaled coordinates Vec3crd m_size; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index b1a25829b..4afc22667 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -108,9 +108,7 @@ static t_config_enum_values s_keys_map_InfillPattern { { "octagramspiral", ipOctagramSpiral }, { "adaptivecubic", ipAdaptiveCubic }, { "supportcubic", ipSupportCubic }, -#if HAS_LIGHTNING_INFILL { "lightning", ipLightning } -#endif // HAS_LIGHTNING_INFILL }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern) @@ -1138,9 +1136,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("octagramspiral"); def->enum_values.push_back("adaptivecubic"); def->enum_values.push_back("supportcubic"); -#if HAS_LIGHTNING_INFILL def->enum_values.push_back("lightning"); -#endif // HAS_LIGHTNING_INFILL def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Aligned Rectilinear")); def->enum_labels.push_back(L("Grid")); @@ -1157,9 +1153,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Octagram Spiral")); def->enum_labels.push_back(L("Adaptive Cubic")); def->enum_labels.push_back(L("Support Cubic")); -#if HAS_LIGHTNING_INFILL def->enum_labels.push_back(L("Lightning")); -#endif // HAS_LIGHTNING_INFILL def->set_default_value(new ConfigOptionEnum(ipStars)); def = this->add("first_layer_acceleration", coFloat); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 2cc758e7b..c473cde4c 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -57,14 +57,10 @@ enum class FuzzySkinType { All, }; -#define HAS_LIGHTNING_INFILL 0 - enum InfillPattern : int { ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, -#if HAS_LIGHTNING_INFILL - ipLightning, -#endif // HAS_LIGHTNING_INFILL + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, + ipLightning, ipCount, }; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 31d948db9..9c6178f8b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -14,6 +14,7 @@ #include "TriangleMeshSlicer.hpp" #include "Utils.hpp" #include "Fill/FillAdaptive.hpp" +#include "Fill/FillLightning.hpp" #include "Format/STL.hpp" #include @@ -353,14 +354,15 @@ void PrintObject::infill() if (this->set_started(posInfill)) { auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); + auto lightning_generator = this->prepare_lightning_infill_data(); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range& range) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &lightning_generator](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get()); + m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), lightning_generator.get()); } } ); @@ -453,6 +455,18 @@ std::pair PrintObject::prepare support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr()); } +FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() +{ + bool has_lightning_infill = false; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) + if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning) { + has_lightning_infill = true; + break; + } + + return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this)) : FillLightning::GeneratorPtr(); +} + void PrintObject::clear_layers() { for (Layer *l : m_layers) From ada55053d8f12dd893fe3726279d52faa6366680 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 21 Apr 2022 15:36:35 +0200 Subject: [PATCH 02/14] Fixed MSVC debug assert in lightning infill (invalidated iterator) --- src/libslic3r/Fill/Lightning/TreeNode.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index b36a66dbb..d1820410e 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -375,8 +375,9 @@ void Node::convertToPolylines(size_t long_line_idx, Polygons& output) const void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const { const coord_t reduction = line_width / 2; // TODO make configurable? - for (auto poly_it = result_lines.begin(); poly_it != result_lines.end(); ) { - Polygon &polyline = *poly_it; + size_t res_line_idx = 0; + while (res_line_idx < result_lines.size()) { + Polygon &polyline = result_lines[res_line_idx]; if (polyline.size() <= 1) { polyline = std::move(result_lines.back()); result_lines.pop_back(); @@ -403,7 +404,7 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt polyline = std::move(result_lines.back()); result_lines.pop_back(); } else - ++ poly_it; + ++ res_line_idx; } } From 8d0dd13144788b590e97e9fdecf91f0b7de89e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 29 Apr 2022 08:07:30 +0200 Subject: [PATCH 03/14] Fixed a crash in Lightning infill. --- src/libslic3r/Fill/Lightning/Generator.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index 6a4a675cb..e0cf9680a 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -116,8 +116,12 @@ void Generator::generateTrees(const PrintObject &print_object) if (layer_id == 0) return; - const Polygons& below_outlines = infill_outlines[layer_id - 1]; - outlines_locator.set_bbox(get_extents(below_outlines).inflated(SCALED_EPSILON)); + const Polygons &below_outlines = infill_outlines[layer_id - 1]; + BoundingBox below_outlines_bbox = get_extents(below_outlines).inflated(SCALED_EPSILON); + if (const BoundingBox &outlines_locator_bbox = outlines_locator.bbox(); outlines_locator_bbox.defined) + below_outlines_bbox.merge(outlines_locator_bbox); + + outlines_locator.set_bbox(below_outlines_bbox); outlines_locator.create(below_outlines, locator_cell_size); std::vector& lower_trees = m_lightning_layers[layer_id - 1].tree_roots; From dfb7fbbd109e1200b1ce4209c8ddac8fd4819913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 29 Apr 2022 08:08:10 +0200 Subject: [PATCH 04/14] Fixed missing layers of Lightning infill. --- src/libslic3r/Fill/Lightning/Layer.cpp | 31 +++++------------------ src/libslic3r/Fill/Lightning/TreeNode.cpp | 12 ++++----- src/libslic3r/Fill/Lightning/TreeNode.hpp | 6 ++--- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index 1e1127a79..c996b9b7b 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -6,6 +6,7 @@ #include "DistanceField.hpp" #include "TreeNode.hpp" +#include "../../ClipperUtils.hpp" #include "../../Geometry.hpp" #include "Utils.hpp" @@ -271,6 +272,7 @@ void Layer::reconnectRoots } } +#if 0 /*! * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance. * Given a \p distance more than zero, the point will end up inside, and conversely outside. @@ -398,6 +400,7 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan } return static_cast(-1); } +#endif // Returns 'added someting'. Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const @@ -405,31 +408,11 @@ Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t if (tree_roots.empty()) return {}; - Polygons result_lines; - for (const auto& tree : tree_roots) { - // If even the furthest location in the tree is inside the polygon, the entire tree must be inside of the polygon. - // (Don't take the root as that may be on the edge and cause rounding errors to register as 'outside'.) - constexpr coord_t epsilon = 5; - Point should_be_inside = tree->getLocation(); - moveInside(limit_to_outline, should_be_inside, epsilon, epsilon * epsilon); - if (inside(limit_to_outline, should_be_inside)) - tree->convertToPolylines(result_lines, line_width); - } + Polylines result_lines; + for (const auto &tree : tree_roots) + tree->convertToPolylines(result_lines, line_width); - // TODO: allow for polylines! - Polylines split_lines; - for (Polygon &line : result_lines) { - if (line.size() <= 1) - continue; - Point last = line[0]; - for (size_t point_idx = 1; point_idx < line.size(); point_idx++) { - Point here = line[point_idx]; - split_lines.push_back({ last, here }); - last = here; - } - } - - return split_lines; + return intersection_pl(result_lines, limit_to_outline); } } // namespace Slic3r::Lightning diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index d1820410e..822550fc4 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -343,16 +343,16 @@ coord_t Node::prune(const coord_t& pruning_distance) return max_distance_pruned; } -void Node::convertToPolylines(Polygons& output, const coord_t line_width) const +void Node::convertToPolylines(Polylines &output, const coord_t line_width) const { - Polygons result; + Polylines result; result.emplace_back(); convertToPolylines(0, result); removeJunctionOverlap(result, line_width); append(output, std::move(result)); } -void Node::convertToPolylines(size_t long_line_idx, Polygons& output) const +void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const { if (m_children.empty()) { output[long_line_idx].points.push_back(m_p); @@ -372,12 +372,12 @@ void Node::convertToPolylines(size_t long_line_idx, Polygons& output) const } } -void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const +void Node::removeJunctionOverlap(Polylines &result_lines, const coord_t line_width) const { const coord_t reduction = line_width / 2; // TODO make configurable? size_t res_line_idx = 0; while (res_line_idx < result_lines.size()) { - Polygon &polyline = result_lines[res_line_idx]; + Polyline &polyline = result_lines[res_line_idx]; if (polyline.size() <= 1) { polyline = std::move(result_lines.back()); result_lines.pop_back(); @@ -387,7 +387,7 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt coord_t to_be_reduced = reduction; Point a = polyline.back(); for (int point_idx = int(polyline.size()) - 2; point_idx >= 0; point_idx--) { - const Point b = polyline[point_idx]; + const Point b = polyline.points[point_idx]; const Point ab = b - a; const auto ab_len = coord_t(ab.cast().norm()); if (ab_len >= to_be_reduced) { diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp index 55ef35e0a..fdb80d2e6 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.hpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp @@ -239,7 +239,7 @@ public: * * \param output all branches in this tree connected into polylines */ - void convertToPolylines(Polygons& output, coord_t line_width) const; + void convertToPolylines(Polylines &output, coord_t line_width) const; /*! If this was ever a direct child of the root, it'll have a previous grounding location. * @@ -258,9 +258,9 @@ protected: * \param long_line a reference to a polyline in \p output which to continue building on in the recursion * \param output all branches in this tree connected into polylines */ - void convertToPolylines(size_t long_line_idx, Polygons& output) const; + void convertToPolylines(size_t long_line_idx, Polylines &output) const; - void removeJunctionOverlap(Polygons& polylines, coord_t line_width) const; + void removeJunctionOverlap(Polylines &polylines, coord_t line_width) const; bool m_is_root; Point m_p; From 78f62bfddc47806263f8d165eb15b94e547a9412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 29 Apr 2022 08:09:58 +0200 Subject: [PATCH 05/14] Fix of #8227 (Lightning infill wasn't working when "Combine infill every X layers" was set to a different value than one.) --- src/libslic3r/Fill/Lightning/Generator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index e0cf9680a..0bdd1c7e8 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -63,7 +63,7 @@ void Generator::generateInitialInternalOverhangs(const PrintObject &print_object Polygons infill_area_here; for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions()) for (const Surface& surface : layerm->fill_surfaces.surfaces) - if (surface.surface_type == stInternal) + if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) append(infill_area_here, infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset)); //Remove the part of the infill area that is already supported by the walls. @@ -92,7 +92,7 @@ void Generator::generateTrees(const PrintObject &print_object) for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions()) for (const Surface &surface : layerm->fill_surfaces.surfaces) - if (surface.surface_type == stInternal) + if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) append(infill_outlines[layer_id], infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset)); // For various operations its beneficial to quickly locate nearby features on the polygon: From 11004b4bbda8f8e41ec4286448896df124993c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 12 May 2022 09:49:07 +0200 Subject: [PATCH 06/14] Added cancellation to Lightning infill. --- src/libslic3r/Fill/FillLightning.cpp | 4 ++-- src/libslic3r/Fill/FillLightning.hpp | 2 +- src/libslic3r/Fill/Lightning/Generator.cpp | 20 ++++++++++++-------- src/libslic3r/Fill/Lightning/Generator.hpp | 7 +++---- src/libslic3r/Fill/Lightning/Layer.cpp | 5 ++++- src/libslic3r/Fill/Lightning/Layer.hpp | 3 ++- src/libslic3r/PrintObject.cpp | 2 +- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Fill/FillLightning.cpp b/src/libslic3r/Fill/FillLightning.cpp index 447fd8057..2ba6fe017 100644 --- a/src/libslic3r/Fill/FillLightning.cpp +++ b/src/libslic3r/Fill/FillLightning.cpp @@ -21,9 +21,9 @@ void GeneratorDeleter::operator()(Generator *p) { delete p; } -GeneratorPtr build_generator(const PrintObject &print_object) +GeneratorPtr build_generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { - return GeneratorPtr(new Generator(print_object)); + return GeneratorPtr(new Generator(print_object, throw_on_cancel_callback)); } } // namespace Slic3r::FillAdaptive diff --git a/src/libslic3r/Fill/FillLightning.hpp b/src/libslic3r/Fill/FillLightning.hpp index b4e7e35f1..941392103 100644 --- a/src/libslic3r/Fill/FillLightning.hpp +++ b/src/libslic3r/Fill/FillLightning.hpp @@ -14,7 +14,7 @@ class Generator; struct GeneratorDeleter { void operator()(Generator *p); }; using GeneratorPtr = std::unique_ptr; -GeneratorPtr build_generator(const PrintObject &print_object); +GeneratorPtr build_generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback); class Filler : public Slic3r::Fill { diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index 0bdd1c7e8..b4c07a338 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -25,7 +25,7 @@ namespace Slic3r::FillLightning { -Generator::Generator(const PrintObject &print_object) +Generator::Generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { const PrintConfig &print_config = print_object.print()->config(); const PrintObjectConfig &object_config = print_object.config(); @@ -47,11 +47,11 @@ Generator::Generator(const PrintObject &print_object) m_prune_length = coord_t(layer_thickness * std::tan(lightning_infill_prune_angle)); m_straightening_max_distance = coord_t(layer_thickness * std::tan(lightning_infill_straightening_angle)); - generateInitialInternalOverhangs(print_object); - generateTrees(print_object); + generateInitialInternalOverhangs(print_object, throw_on_cancel_callback); + generateTrees(print_object, throw_on_cancel_callback); } -void Generator::generateInitialInternalOverhangs(const PrintObject &print_object) +void Generator::generateInitialInternalOverhangs(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { m_overhang_per_layer.resize(print_object.layers().size()); // FIXME: It can be adjusted to improve bonding between infill and perimeters. @@ -59,7 +59,8 @@ void Generator::generateInitialInternalOverhangs(const PrintObject &print_object Polygons infill_area_above; //Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging. - for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; layer_nr--) { + for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; --layer_nr) { + throw_on_cancel_callback(); Polygons infill_area_here; for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions()) for (const Surface& surface : layerm->fill_surfaces.surfaces) @@ -80,7 +81,7 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const return m_lightning_layers[layer_id]; } -void Generator::generateTrees(const PrintObject &print_object) +void Generator::generateTrees(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { m_lightning_layers.resize(print_object.layers().size()); // FIXME: It can be adjusted to improve bonding between infill and perimeters. @@ -89,11 +90,13 @@ void Generator::generateTrees(const PrintObject &print_object) std::vector infill_outlines(print_object.layers().size(), Polygons()); // For-each layer from top to bottom: - for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) + for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) { + throw_on_cancel_callback(); for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions()) for (const Surface &surface : layerm->fill_surfaces.surfaces) if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) append(infill_outlines[layer_id], infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset)); + } // For various operations its beneficial to quickly locate nearby features on the polygon: const size_t top_layer_id = print_object.layers().size() - 1; @@ -102,6 +105,7 @@ void Generator::generateTrees(const PrintObject &print_object) // For-each layer from top to bottom: for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) { + throw_on_cancel_callback(); Layer ¤t_lightning_layer = m_lightning_layers[layer_id]; const Polygons ¤t_outlines = infill_outlines[layer_id]; const BoundingBox ¤t_outlines_bbox = get_extents(current_outlines); @@ -109,7 +113,7 @@ void Generator::generateTrees(const PrintObject &print_object) // register all trees propagated from the previous layer as to-be-reconnected std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; - current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius, throw_on_cancel_callback); current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); // Initialize trees for next lower layer from the current one. diff --git a/src/libslic3r/Fill/Lightning/Generator.hpp b/src/libslic3r/Fill/Lightning/Generator.hpp index b538c4dbe..d7e5f63f9 100644 --- a/src/libslic3r/Fill/Lightning/Generator.hpp +++ b/src/libslic3r/Fill/Lightning/Generator.hpp @@ -43,9 +43,8 @@ public: * This generator will pre-compute things in preparation of generating * Lightning Infill for the infill areas in that mesh. The infill areas must * already be calculated at this point. - * \param mesh The mesh to generate infill for. */ - explicit Generator(const PrintObject &print_object); + explicit Generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback); /*! * Get a tree of paths generated for a certain layer of the mesh. @@ -69,12 +68,12 @@ protected: * only when support is generated. For this pattern, we also need to * generate overhang areas for the inside of the model. */ - void generateInitialInternalOverhangs(const PrintObject &print_object); + void generateInitialInternalOverhangs(const PrintObject &print_object, const std::function &throw_on_cancel_callback); /*! * Calculate the tree structure of all layers. */ - void generateTrees(const PrintObject &print_object); + void generateTrees(const PrintObject &print_object, const std::function &throw_on_cancel_callback); float m_infill_extrusion_width; diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index c996b9b7b..c381d5505 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -44,10 +44,12 @@ void Layer::generateNewTrees const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outlines_locator, const coord_t supporting_radius, - const coord_t wall_supporting_radius + const coord_t wall_supporting_radius, + const std::function &throw_on_cancel_callback ) { DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang); + throw_on_cancel_callback(); SparseNodeGrid tree_node_locator; fillLocator(tree_node_locator, current_outlines_bbox); @@ -56,6 +58,7 @@ void Layer::generateNewTrees // Determine next point from tree/outline areas via distance-field Point unsupported_location; while (distance_field.tryGetNextPoint(&unsupported_location)) { + throw_on_cancel_callback(); GroundingLocation grounding_loc = getBestGroundingLocation( unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp index cc79c15b5..87431fb1c 100644 --- a/src/libslic3r/Fill/Lightning/Layer.hpp +++ b/src/libslic3r/Fill/Lightning/Layer.hpp @@ -44,7 +44,8 @@ public: const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, coord_t supporting_radius, - coord_t wall_supporting_radius + coord_t wall_supporting_radius, + const std::function &throw_on_cancel_callback ); /*! Determine & connect to connection point in tree/outline. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 9c6178f8b..eeaf1b13c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -464,7 +464,7 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() break; } - return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this)) : FillLightning::GeneratorPtr(); + return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this), [this]() -> void { this->throw_if_canceled(); }) : FillLightning::GeneratorPtr(); } void PrintObject::clear_layers() From 331da74510b37787a554492883c613d574818559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 12 May 2022 14:16:32 +0200 Subject: [PATCH 07/14] Fixed function lineSegmentPolygonsIntersection() in the Lightning infill that wasn't returns the intersection point. It fixed the issue that sometimes the Lightning infill produced extrusions that hung in the air without proper support. --- src/libslic3r/Fill/Lightning/TreeNode.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index 822550fc4..9ef509611 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -180,7 +180,11 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG } visitor { outline_locator, a.cast(), b.cast() }; outline_locator.visit_cells_intersecting_line(a, b, visitor); - return visitor.d2min < double(within_max_dist) * double(within_max_dist); + if (visitor.d2min < double(within_max_dist) * double(within_max_dist)) { + result = Point(visitor.intersection_pt); + return true; + } + return false; } bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts) From 3e5272868ddf1eed6c82291bd3a25a9774f6e0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 17 May 2022 12:41:28 +0200 Subject: [PATCH 08/14] Fixed an issue that some trees in the Lightning infill weren't connected to perimeters. --- src/libslic3r/Fill/Lightning/Layer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index c381d5505..f3193afe4 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -129,9 +129,10 @@ GroundingLocation Layer::getBestGroundingLocation if (contour.size() > 2) { Point prev = contour.points.back(); for (const Point &p2 : contour.points) { - if (double d = Line::distance_to_squared(unsupported_location, prev, p2); d < d2) { + Point closest_point; + if (double d = line_alg::distance_to_squared(Line{prev, p2}, unsupported_location, &closest_point); d < d2) { d2 = d; - node_location = Geometry::foot_pt({ prev, p2 }, unsupported_location).cast(); + node_location = closest_point; } prev = p2; } From 5205753787340f68d43f73bf41a6d4fc4dbfcab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 19 May 2022 12:46:15 +0200 Subject: [PATCH 09/14] Refactored Lightning infill to get rid of unnecessary std::list and std::unordered_map, which was slow. --- .../Fill/Lightning/DistanceField.cpp | 21 ++-- .../Fill/Lightning/DistanceField.hpp | 98 +++++++++++++++++-- src/libslic3r/Fill/Lightning/Layer.cpp | 5 +- 3 files changed, 105 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index 308ca41c6..d956c4e23 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -35,16 +35,18 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl assert(m_unsupported_points_bbox.contains(p)); } } - m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) { + std::stable_sort(m_unsupported_points.begin(), m_unsupported_points.end(), [&radius](const UnsupportedCell &a, const UnsupportedCell &b) { constexpr coord_t prime_for_hash = 191; return std::abs(b.dist_to_boundary - a.dist_to_boundary) > radius ? a.dist_to_boundary < b.dist_to_boundary : (PointHash{}(a.loc) % prime_for_hash) < (PointHash{}(b.loc) % prime_for_hash); }); - for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) { - UnsupportedCell& cell = *it; - m_unsupported_points_grid.emplace(this->to_grid_point(cell.loc), it); - } + + m_unsupported_points_erased.resize(m_unsupported_points.size()); + std::fill(m_unsupported_points_erased.begin(), m_unsupported_points_erased.end(), false); + + m_unsupported_points_grid.initialize(m_unsupported_points, [&self = std::as_const(*this)](const Point &p) -> Point { return self.to_grid_point(p); }); + // Because the distance between two points is at least one axis equal to m_cell_size, every cell // in m_unsupported_points_grid contains exactly one point. assert(m_unsupported_points.size() == m_unsupported_points_grid.size()); @@ -96,12 +98,11 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf) } // Inside a circle at the end of the new leaf, or inside a rotated rectangle. // Remove unsupported leafs at this grid location. - if (auto it = m_unsupported_points_grid.find(grid_addr); it != m_unsupported_points_grid.end()) { - std::list::iterator& list_it = it->second; - UnsupportedCell& cell = *list_it; + if (const size_t cell_idx = m_unsupported_points_grid.find_cell_idx(grid_addr); cell_idx != std::numeric_limits::max()) { + const UnsupportedCell &cell = m_unsupported_points[cell_idx]; if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { - m_unsupported_points.erase(list_it); - m_unsupported_points_grid.erase(it); + m_unsupported_points_erased[cell_idx] = true; + m_unsupported_points_grid.mark_erased(grid_addr); } } } diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index beb46c5c5..fc25beb62 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -38,11 +38,17 @@ public: * \return ``true`` if successful, or ``false`` if there are no more points * to consider. */ - bool tryGetNextPoint(Point* p) const { - if (m_unsupported_points.empty()) - return false; - *p = m_unsupported_points.front().loc; - return true; + bool tryGetNextPoint(Point *out_unsupported_location, size_t *out_unsupported_cell_idx, const size_t start_idx = 0) const + { + for (size_t point_idx = start_idx; point_idx < m_unsupported_points.size(); ++point_idx) { + if (!m_unsupported_points_erased[point_idx]) { + *out_unsupported_cell_idx = point_idx; + *out_unsupported_location = m_unsupported_points[point_idx].loc; + return true; + } + } + + return false; } /*! @@ -87,7 +93,8 @@ protected: /*! * Cells which still need to be supported at some point. */ - std::list m_unsupported_points; + std::vector m_unsupported_points; + std::vector m_unsupported_points_erased; /*! * BoundingBox of all points in m_unsupported_points. Used for mapping of sign integer numbers to positive integer numbers. @@ -98,7 +105,84 @@ protected: * Links the unsupported points to a grid point, so that we can quickly look * up the cell belonging to a certain position in the grid. */ - std::unordered_map::iterator, PointHash> m_unsupported_points_grid; + + class UnsupportedPointsGrid + { + public: + UnsupportedPointsGrid() = default; + void initialize(const std::vector &unsupported_points, const std::function &map_cell_to_grid) + { + if (unsupported_points.empty()) + return; + + BoundingBox unsupported_points_bbox; + for (const UnsupportedCell &cell : unsupported_points) + unsupported_points_bbox.merge(cell.loc); + + m_size = unsupported_points.size(); + m_grid_range = BoundingBox(map_cell_to_grid(unsupported_points_bbox.min), map_cell_to_grid(unsupported_points_bbox.max)); + m_grid_size = m_grid_range.size() + Point::Ones(); + + m_data.assign(m_grid_size.y() * m_grid_size.x(), std::numeric_limits::max()); + m_data_erased.assign(m_grid_size.y() * m_grid_size.x(), true); + + for (size_t cell_idx = 0; cell_idx < unsupported_points.size(); ++cell_idx) { + const size_t flat_idx = map_to_flat_array(map_cell_to_grid(unsupported_points[cell_idx].loc)); + assert(m_data[flat_idx] == std::numeric_limits::max()); + m_data[flat_idx] = cell_idx; + m_data_erased[flat_idx] = false; + } + } + + size_t size() const { return m_size; } + + size_t find_cell_idx(const Point &grid_addr) + { + if (!m_grid_range.contains(grid_addr)) + return std::numeric_limits::max(); + + if (const size_t flat_idx = map_to_flat_array(grid_addr); !m_data_erased[flat_idx]) { + assert(m_data[flat_idx] != std::numeric_limits::max()); + return m_data[flat_idx]; + } + + return std::numeric_limits::max(); + } + + void mark_erased(const Point &grid_addr) + { + assert(m_grid_range.contains(grid_addr)); + if (!m_grid_range.contains(grid_addr)) + return; + + const size_t flat_idx = map_to_flat_array(grid_addr); + assert(!m_data_erased[flat_idx] && m_data[flat_idx] != std::numeric_limits::max()); + assert(m_size != 0); + + m_data_erased[flat_idx] = true; + --m_size; + } + + private: + size_t m_size = 0; + + BoundingBox m_grid_range; + Point m_grid_size; + + std::vector m_data; + std::vector m_data_erased; + + inline size_t map_to_flat_array(const Point &loc) const + { + const Point offset_loc = loc - m_grid_range.min; + const size_t flat_idx = m_grid_size.x() * offset_loc.y() + offset_loc.x(); + assert(offset_loc.x() >= 0 && offset_loc.y() >= 0); + assert(flat_idx < m_grid_size.y() * m_grid_size.x()); + return flat_idx; + } + }; + + UnsupportedPointsGrid m_unsupported_points_grid; /*! * Maps the point to the grid coordinates. diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index f3193afe4..e8f954a60 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -56,8 +56,9 @@ void Layer::generateNewTrees // Until no more points need to be added to support all: // Determine next point from tree/outline areas via distance-field - Point unsupported_location; - while (distance_field.tryGetNextPoint(&unsupported_location)) { + size_t unsupported_cell_idx = 0; + Point unsupported_location; + while (distance_field.tryGetNextPoint(&unsupported_location, &unsupported_cell_idx, unsupported_cell_idx)) { throw_on_cancel_callback(); GroundingLocation grounding_loc = getBestGroundingLocation( unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); From be291fd97ec09ac096dc60f2d98427de8667e81d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 19 May 2022 12:46:41 +0200 Subject: [PATCH 10/14] Refactored Lightning infill before parallelization. --- .../Fill/Lightning/DistanceField.cpp | 11 +++++++--- .../Fill/Lightning/DistanceField.hpp | 1 - src/libslic3r/Fill/Lightning/Layer.cpp | 21 +++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index d956c4e23..3602e60ab 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -18,7 +18,12 @@ 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. for (const ExPolygon &expoly : union_ex(current_overhang)) { - for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) { + const Points sampled_points = sample_grid_pattern(expoly, m_cell_size); + const size_t unsupported_points_prev_size = m_unsupported_points.size(); + m_unsupported_points.resize(unsupported_points_prev_size + sampled_points.size()); + + for (size_t sp_idx = 0; sp_idx < sampled_points.size(); ++sp_idx) { + const Point &sp = sampled_points[sp_idx]; // Find a squared distance to the source expolygon boundary. double d2 = std::numeric_limits::max(); for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) { @@ -26,12 +31,12 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl if (contour.size() > 2) { Point prev = contour.points.back(); for (const Point &p2 : contour.points) { - d2 = std::min(d2, Line::distance_to_squared(p, prev, p2)); + d2 = std::min(d2, Line::distance_to_squared(sp, prev, p2)); prev = p2; } } } - m_unsupported_points.emplace_back(p, sqrt(d2)); + m_unsupported_points[unsupported_points_prev_size + sp_idx] = {sp, coord_t(std::sqrt(d2))}; assert(m_unsupported_points_bbox.contains(p)); } } diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index fc25beb62..d4a142c05 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -83,7 +83,6 @@ protected: */ struct UnsupportedCell { - UnsupportedCell(const Point &loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} // The position of the center of this cell. Point loc; // How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area. diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index e8f954a60..8539bb532 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -142,26 +142,25 @@ GroundingLocation Layer::getBestGroundingLocation const auto within_dist = coord_t((node_location - unsupported_location).cast().norm()); - NodeSPtr sub_tree{ nullptr }; - coord_t current_dist = getWeightedDistance(node_location, unsupported_location); + NodeSPtr sub_tree{nullptr}; + coord_t current_dist = getWeightedDistance(node_location, unsupported_location); if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines. const coord_t search_radius = std::min(current_dist, within_dist); BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size)); region.min = to_grid_point(region.min, current_outlines_bbox); region.max = to_grid_point(region.max, current_outlines_bbox); - Point grid_addr; - for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y()) - for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) { - auto it_range = tree_node_locator.equal_range(grid_addr); - for (auto it = it_range.first; it != it_range.second; ++ it) { - auto candidate_sub_tree = it->second.lock(); + + for (coord_t grid_addr_y = region.min.y(); grid_addr_y < region.max.y(); ++grid_addr_y) + for (coord_t grid_addr_x = region.min.x(); grid_addr_x < region.max.x(); ++grid_addr_x) { + const auto it_range = tree_node_locator.equal_range({grid_addr_x, grid_addr_y}); + for (auto it = it_range.first; it != it_range.second; ++it) { + const NodeSPtr candidate_sub_tree = it->second.lock(); if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) && !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) && !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) { - const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); - if (candidate_dist < current_dist) { + if (const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); candidate_dist < current_dist) { current_dist = candidate_dist; - sub_tree = candidate_sub_tree; + sub_tree = candidate_sub_tree; } } } From 76deba7e978d6d981a37f7361ba866f5da6e5f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 19 May 2022 12:47:25 +0200 Subject: [PATCH 11/14] Parallelized DistanceField::DistanceField() and Layer::getBestGroundingLocation() in Lightning infill. --- .../Fill/Lightning/DistanceField.cpp | 32 +++++++----- src/libslic3r/Fill/Lightning/Layer.cpp | 51 ++++++++++++++----- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index 3602e60ab..ef407664c 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -5,6 +5,8 @@ #include "../FillRectilinear.hpp" #include "../../ClipperUtils.hpp" +#include + namespace Slic3r::FillLightning { @@ -22,23 +24,25 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl const size_t unsupported_points_prev_size = m_unsupported_points.size(); m_unsupported_points.resize(unsupported_points_prev_size + sampled_points.size()); - for (size_t sp_idx = 0; sp_idx < sampled_points.size(); ++sp_idx) { - const Point &sp = sampled_points[sp_idx]; - // Find a squared distance to the source expolygon boundary. - double d2 = std::numeric_limits::max(); - for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) { - const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1]; - if (contour.size() > 2) { - Point prev = contour.points.back(); - for (const Point &p2 : contour.points) { - d2 = std::min(d2, Line::distance_to_squared(sp, prev, p2)); - prev = p2; + tbb::parallel_for(tbb::blocked_range(0, sampled_points.size()), [&self = *this, &expoly = std::as_const(expoly), &sampled_points = std::as_const(sampled_points), &unsupported_points_prev_size = std::as_const(unsupported_points_prev_size)](const tbb::blocked_range &range) -> void { + for (size_t sp_idx = range.begin(); sp_idx < range.end(); ++sp_idx) { + const Point &sp = sampled_points[sp_idx]; + // Find a squared distance to the source expolygon boundary. + double d2 = std::numeric_limits::max(); + for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) { + const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1]; + if (contour.size() > 2) { + Point prev = contour.points.back(); + for (const Point &p2 : contour.points) { + d2 = std::min(d2, Line::distance_to_squared(sp, prev, p2)); + prev = p2; + } } } + self.m_unsupported_points[unsupported_points_prev_size + sp_idx] = {sp, coord_t(std::sqrt(d2))}; + assert(self.m_unsupported_points_bbox.contains(sp)); } - m_unsupported_points[unsupported_points_prev_size + sp_idx] = {sp, coord_t(std::sqrt(d2))}; - assert(m_unsupported_points_bbox.contains(p)); - } + }); // end of parallel_for } std::stable_sort(m_unsupported_points.begin(), m_unsupported_points.end(), [&radius](const UnsupportedCell &a, const UnsupportedCell &b) { constexpr coord_t prime_for_hash = 191; diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index 8539bb532..0bd2a65c4 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -10,6 +10,10 @@ #include "../../Geometry.hpp" #include "Utils.hpp" +#include +#include +#include + namespace Slic3r::FillLightning { coord_t Layer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location) @@ -150,21 +154,44 @@ GroundingLocation Layer::getBestGroundingLocation region.min = to_grid_point(region.min, current_outlines_bbox); region.max = to_grid_point(region.max, current_outlines_bbox); - for (coord_t grid_addr_y = region.min.y(); grid_addr_y < region.max.y(); ++grid_addr_y) - for (coord_t grid_addr_x = region.min.x(); grid_addr_x < region.max.x(); ++grid_addr_x) { - const auto it_range = tree_node_locator.equal_range({grid_addr_x, grid_addr_y}); - for (auto it = it_range.first; it != it_range.second; ++it) { - const NodeSPtr candidate_sub_tree = it->second.lock(); - if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) && - !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) && - !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) { - if (const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); candidate_dist < current_dist) { - current_dist = candidate_dist; - sub_tree = candidate_sub_tree; + Point current_dist_grid_addr{std::numeric_limits::lowest(), std::numeric_limits::lowest()}; + std::mutex current_dist_mutex; + tbb::parallel_for(tbb::blocked_range2d(region.min.y(), region.max.y(), region.min.x(), region.max.x()), [¤t_dist, current_dist_copy = current_dist, ¤t_dist_mutex, &sub_tree, ¤t_dist_grid_addr, &exclude_tree = std::as_const(exclude_tree), &outline_locator = std::as_const(outline_locator), &supporting_radius = std::as_const(supporting_radius), &tree_node_locator = std::as_const(tree_node_locator), &unsupported_location = std::as_const(unsupported_location)](const tbb::blocked_range2d &range) -> void { + for (coord_t grid_addr_y = range.rows().begin(); grid_addr_y < range.rows().end(); ++grid_addr_y) + for (coord_t grid_addr_x = range.cols().begin(); grid_addr_x < range.cols().end(); ++grid_addr_x) { + const Point local_grid_addr{grid_addr_x, grid_addr_y}; + NodeSPtr local_sub_tree{nullptr}; + coord_t local_current_dist = current_dist_copy; + const auto it_range = tree_node_locator.equal_range(local_grid_addr); + for (auto it = it_range.first; it != it_range.second; ++it) { + const NodeSPtr candidate_sub_tree = it->second.lock(); + if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) && + !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) && + !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) { + if (const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); candidate_dist < local_current_dist) { + local_current_dist = candidate_dist; + local_sub_tree = candidate_sub_tree; + } + } + } + // To always get the same result in a parallel version as in a non-parallel version, + // we need to preserve that for the same current_dist, we select the same sub_tree + // as in the non-parallel version. For this purpose, inside the variable + // current_dist_grid_addr is stored from with 2D grid position assigned sub_tree comes. + // And when there are two sub_tree with the same current_dist, one which will be found + // the first in the non-parallel version is selected. + { + std::lock_guard lock(current_dist_mutex); + if (local_current_dist < current_dist || + (local_current_dist == current_dist && (grid_addr_y < current_dist_grid_addr.y() || + (grid_addr_y == current_dist_grid_addr.y() && grid_addr_x < current_dist_grid_addr.x())))) { + current_dist = local_current_dist; + sub_tree = local_sub_tree; + current_dist_grid_addr = local_grid_addr; } } } - } + }); // end of parallel_for } return ! sub_tree ? From a5270f3c5a13987eb48b30bdb53d2d7c7a41f172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 19 May 2022 15:08:50 +0200 Subject: [PATCH 12/14] Fixed another crash in Lightning infill. --- src/libslic3r/Fill/Lightning/Generator.cpp | 2 ++ src/libslic3r/Fill/Lightning/TreeNode.hpp | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index b4c07a338..e226fbbab 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -125,6 +125,8 @@ void Generator::generateTrees(const PrintObject &print_object, const std::functi if (const BoundingBox &outlines_locator_bbox = outlines_locator.bbox(); outlines_locator_bbox.defined) below_outlines_bbox.merge(outlines_locator_bbox); + below_outlines_bbox.merge(get_extents(current_lightning_layer.tree_roots).inflated(SCALED_EPSILON)); + outlines_locator.set_bbox(below_outlines_bbox); outlines_locator.create(below_outlines, locator_cell_size); diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp index fdb80d2e6..81c63f7f6 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.hpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp @@ -269,6 +269,9 @@ protected: std::optional m_last_grounding_location; // &tree_roots); + #ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT friend void export_to_svg(const NodeSPtr &root_node, Slic3r::SVG &svg); friend void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes); @@ -278,6 +281,23 @@ protected: bool inside(const Polygons &polygons, const Point &p); bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, coord_t within_max_dist); +inline BoundingBox get_extents(const NodeSPtr &root_node) +{ + BoundingBox bbox; + for (const NodeSPtr &children : root_node->m_children) + bbox.merge(get_extents(children)); + bbox.merge(root_node->getLocation()); + return bbox; +} + +inline BoundingBox get_extents(const std::vector &tree_roots) +{ + BoundingBox bbox; + for (const NodeSPtr &root_node : tree_roots) + bbox.merge(get_extents(root_node)); + return bbox; +} + #ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT void export_to_svg(const NodeSPtr &root_node, SVG &svg); void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes); From 79b8acb7d5e39302ff11222ae158b689ad411ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 23 May 2022 12:41:23 +0200 Subject: [PATCH 13/14] Fixed assert in Lightning infill (merging empty BoundingBoxes). --- src/libslic3r/Fill/Lightning/Generator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index e226fbbab..5e2838e46 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -125,7 +125,8 @@ void Generator::generateTrees(const PrintObject &print_object, const std::functi if (const BoundingBox &outlines_locator_bbox = outlines_locator.bbox(); outlines_locator_bbox.defined) below_outlines_bbox.merge(outlines_locator_bbox); - below_outlines_bbox.merge(get_extents(current_lightning_layer.tree_roots).inflated(SCALED_EPSILON)); + if (!current_lightning_layer.tree_roots.empty()) + below_outlines_bbox.merge(get_extents(current_lightning_layer.tree_roots).inflated(SCALED_EPSILON)); outlines_locator.set_bbox(below_outlines_bbox); outlines_locator.create(below_outlines, locator_cell_size); From bcd20650bff939eb749efca316cebac389289161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 24 May 2022 14:07:37 +0200 Subject: [PATCH 14/14] Added anchors for the Lightning infill to better connect the infill and perimeters. --- src/libslic3r/Fill/FillLightning.cpp | 23 +++++++++++-------- src/libslic3r/Fill/FillLightning.hpp | 9 ++++++-- .../Fill/Lightning/DistanceField.hpp | 2 +- src/libslic3r/Fill/Lightning/Generator.cpp | 9 ++------ src/libslic3r/Fill/Lightning/Layer.cpp | 5 ++-- src/libslic3r/Fill/Lightning/Layer.hpp | 2 +- src/libslic3r/Fill/Lightning/TreeNode.cpp | 10 ++++---- src/libslic3r/Fill/Lightning/TreeNode.hpp | 10 ++++---- 8 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Fill/FillLightning.cpp b/src/libslic3r/Fill/FillLightning.cpp index 2ba6fe017..dd2189e6b 100644 --- a/src/libslic3r/Fill/FillLightning.cpp +++ b/src/libslic3r/Fill/FillLightning.cpp @@ -1,20 +1,25 @@ #include "../Print.hpp" +#include "../ShortestPath.hpp" #include "FillLightning.hpp" #include "Lightning/Generator.hpp" -#include "../Surface.hpp" - -#include -#include -#include -#include namespace Slic3r::FillLightning { -Polylines Filler::fill_surface(const Surface *surface, const FillParams ¶ms) +void Filler::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon expolygon, + Polylines &polylines_out) { - const Layer &layer = generator->getTreesForLayer(this->layer_id); - return layer.convertToLines(to_polygons(surface->expolygon), generator->infilll_extrusion_width()); + const Layer &layer = generator->getTreesForLayer(this->layer_id); + Polylines fill_lines = layer.convertToLines(to_polygons(expolygon), scaled(0.5 * this->spacing - this->overlap)); + + if (params.dont_connect() || fill_lines.size() <= 1) { + append(polylines_out, chain_polylines(std::move(fill_lines))); + } else + connect_infill(std::move(fill_lines), expolygon, polylines_out, this->spacing, params); } void GeneratorDeleter::operator()(Generator *p) { diff --git a/src/libslic3r/Fill/FillLightning.hpp b/src/libslic3r/Fill/FillLightning.hpp index 941392103..6e672783a 100644 --- a/src/libslic3r/Fill/FillLightning.hpp +++ b/src/libslic3r/Fill/FillLightning.hpp @@ -24,8 +24,13 @@ public: Generator *generator { nullptr }; protected: Fill* clone() const override { return new Filler(*this); } - // Perform the fill. - Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + + void _fill_surface_single(const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon expolygon, + Polylines &polylines_out) override; + // Let the G-code export reoder the infill lines. bool no_sort() const override { return false; } }; diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index d4a142c05..1a47ee6ca 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -176,7 +176,7 @@ protected: const Point offset_loc = loc - m_grid_range.min; const size_t flat_idx = m_grid_size.x() * offset_loc.y() + offset_loc.x(); assert(offset_loc.x() >= 0 && offset_loc.y() >= 0); - assert(flat_idx < m_grid_size.y() * m_grid_size.x()); + assert(flat_idx < size_t(m_grid_size.y() * m_grid_size.x())); return flat_idx; } }; diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index 5e2838e46..4aba7202d 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -7,7 +7,6 @@ #include "../../ClipperUtils.hpp" #include "../../Layer.hpp" #include "../../Print.hpp" -#include "../../Surface.hpp" /* Possible future tasks/optimizations,etc.: * - Improve connecting heuristic to favor connecting to shorter trees @@ -54,8 +53,6 @@ Generator::Generator(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { m_overhang_per_layer.resize(print_object.layers().size()); - // FIXME: It can be adjusted to improve bonding between infill and perimeters. - const float infill_wall_offset = 0;// m_infill_extrusion_width; Polygons infill_area_above; //Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging. @@ -65,7 +62,7 @@ void Generator::generateInitialInternalOverhangs(const PrintObject &print_object for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions()) for (const Surface& surface : layerm->fill_surfaces.surfaces) if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) - append(infill_area_here, infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset)); + infill_area_here.emplace_back(surface.expolygon); //Remove the part of the infill area that is already supported by the walls. Polygons overhang = diff(offset(infill_area_here, -float(m_wall_supporting_radius)), infill_area_above); @@ -84,8 +81,6 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const void Generator::generateTrees(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { m_lightning_layers.resize(print_object.layers().size()); - // FIXME: It can be adjusted to improve bonding between infill and perimeters. - const coord_t infill_wall_offset = 0;// m_infill_extrusion_width; std::vector infill_outlines(print_object.layers().size(), Polygons()); @@ -95,7 +90,7 @@ void Generator::generateTrees(const PrintObject &print_object, const std::functi for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions()) for (const Surface &surface : layerm->fill_surfaces.surfaces) if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) - append(infill_outlines[layer_id], infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset)); + infill_outlines[layer_id].emplace_back(surface.expolygon); } // For various operations its beneficial to quickly locate nearby features on the polygon: diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index 0bd2a65c4..354623e51 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -433,15 +433,14 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan } #endif -// Returns 'added someting'. -Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const +Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_overlap) const { if (tree_roots.empty()) return {}; Polylines result_lines; for (const auto &tree : tree_roots) - tree->convertToPolylines(result_lines, line_width); + tree->convertToPolylines(result_lines, line_overlap); return intersection_pl(result_lines, limit_to_outline); } diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp index 87431fb1c..e8c0a38b4 100644 --- a/src/libslic3r/Fill/Lightning/Layer.hpp +++ b/src/libslic3r/Fill/Lightning/Layer.hpp @@ -80,7 +80,7 @@ public: coord_t wall_supporting_radius ); - Polylines convertToLines(const Polygons& limit_to_outline, coord_t line_width) const; + Polylines convertToLines(const Polygons& limit_to_outline, coord_t line_overlap) const; coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location); diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index 9ef509611..982d47b10 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -347,12 +347,12 @@ coord_t Node::prune(const coord_t& pruning_distance) return max_distance_pruned; } -void Node::convertToPolylines(Polylines &output, const coord_t line_width) const +void Node::convertToPolylines(Polylines &output, const coord_t line_overlap) const { Polylines result; result.emplace_back(); convertToPolylines(0, result); - removeJunctionOverlap(result, line_width); + removeJunctionOverlap(result, line_overlap); append(output, std::move(result)); } @@ -376,10 +376,10 @@ void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const } } -void Node::removeJunctionOverlap(Polylines &result_lines, const coord_t line_width) const +void Node::removeJunctionOverlap(Polylines &result_lines, const coord_t line_overlap) const { - const coord_t reduction = line_width / 2; // TODO make configurable? - size_t res_line_idx = 0; + const coord_t reduction = line_overlap; + size_t res_line_idx = 0; while (res_line_idx < result_lines.size()) { Polyline &polyline = result_lines[res_line_idx]; if (polyline.size() <= 1) { diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp index 81c63f7f6..8791b4331 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.hpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp @@ -46,7 +46,7 @@ public: { struct EnableMakeShared : public Node { - EnableMakeShared(Arg&&...arg) : Node(std::forward(arg)...) {} + explicit EnableMakeShared(Arg&&...arg) : Node(std::forward(arg)...) {} }; return std::make_shared(std::forward(arg)...); } @@ -179,16 +179,16 @@ public: */ bool hasOffspring(const NodeSPtr& to_be_checked) const; -protected: Node() = delete; // Don't allow empty contruction +protected: /*! * Construct a new node, either for insertion in a tree or as root. * \param p The physical location in the 2D layer that this node represents. * Connecting other nodes to this node indicates that a line segment should * be drawn between those two physical positions. */ - Node(const Point& p, const std::optional& last_grounding_location = std::nullopt); + explicit Node(const Point& p, const std::optional& last_grounding_location = std::nullopt); /*! * Copy this node and its entire sub-tree. @@ -239,7 +239,7 @@ public: * * \param output all branches in this tree connected into polylines */ - void convertToPolylines(Polylines &output, coord_t line_width) const; + void convertToPolylines(Polylines &output, coord_t line_overlap) const; /*! If this was ever a direct child of the root, it'll have a previous grounding location. * @@ -260,7 +260,7 @@ protected: */ void convertToPolylines(size_t long_line_idx, Polylines &output) const; - void removeJunctionOverlap(Polylines &polylines, coord_t line_width) const; + void removeJunctionOverlap(Polylines &polylines, coord_t line_overlap) const; bool m_is_root; Point m_p;