From 98055de28c29a1e572163f601b6cea335ebeb2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= <hejl.lukas@gmail.com> Date: Thu, 3 Dec 2020 09:43:21 +0100 Subject: [PATCH] External paths avoid crossing perimeters of holes --- .../GCode/AvoidCrossingPerimeters.cpp | 133 +++++++++++------- .../GCode/AvoidCrossingPerimeters.hpp | 8 +- 2 files changed, 91 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 7ce3cebc3..8262c3857 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -511,6 +511,27 @@ static float get_perimeter_spacing(const Layer &layer) return perimeter_spacing; } +// called by get_boundary_external() +static float get_perimeter_spacing_external(const Layer &layer) +{ + size_t regions_count = 0; + float perimeter_spacing = 0.f; + for (const PrintObject *object : layer.object()->print()->objects()) + if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l) + for (const LayerRegion *layer_region : l->regions()) + if (layer_region != nullptr && !layer_region->slices.empty()) { + perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing(); + ++ regions_count; + } + + assert(perimeter_spacing >= 0.f); + if (regions_count != 0) + perimeter_spacing /= float(regions_count); + else + perimeter_spacing = get_default_perimeter_spacing(*layer.object()); + return perimeter_spacing; +} + // Adds points around all vertices so that the offset affects only small sections around these vertices. static void resample_polygon(Polygon &polygon, double dist_from_vertex) { @@ -765,13 +786,9 @@ static ExPolygons get_boundary(const Layer &layer) { const float perimeter_spacing = get_perimeter_spacing(layer); const float perimeter_offset = perimeter_spacing / 2.f; - size_t polygons_count = 0; - for (const LayerRegion *layer_region : layer.regions()) - polygons_count += layer_region->slices.surfaces.size(); - - ExPolygons boundary = union_ex(inner_offset(layer.lslices, perimeter_offset)); + ExPolygons boundary = union_ex(inner_offset(layer.lslices, perimeter_offset)); // Collect all top layers that will not be crossed. - polygons_count = 0; + size_t polygons_count = 0; for (const LayerRegion *layer_region : layer.regions()) for (const Surface &surface : layer_region->fill_surfaces.surfaces) if (surface.is_top()) ++polygons_count; @@ -790,6 +807,35 @@ static ExPolygons get_boundary(const Layer &layer) return boundary; } +// called by AvoidCrossingPerimeters::travel_to() +static Polygons get_boundary_external(const Layer &layer) +{ + const float perimeter_spacing = get_perimeter_spacing(layer); + const float perimeter_offset = perimeter_spacing / 2.f; + Polygons boundary; + // Collect all holes for all printed objects and their instances, which will be printed at the same time as passed "layer". + for (const PrintObject *object : layer.object()->print()->objects()) { + Polygons polygons_per_obj; + if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l) + for (const ExPolygon &island : l->lslices) append(polygons_per_obj, island.holes); + + for (const PrintInstance &instance : object->instances()) { + size_t boundary_idx = boundary.size(); + append(boundary, polygons_per_obj); + for (; boundary_idx < boundary.size(); ++boundary_idx) + boundary[boundary_idx].translate(instance.shift); + } + } + + // Used offset_ex for cases when another object will be in the hole of another polygon + boundary = to_polygons(offset_ex(boundary, perimeter_offset)); + // Reverse all polygons for making normals point from the polygon out. + for (Polygon &poly : boundary) + poly.reverse(); + + return boundary; +} + static void init_boundary_distances(AvoidCrossingPerimeters::Boundary *boundary) { boundary->boundaries_params.assign(boundary->boundaries.size(), std::vector<float>()); @@ -797,6 +843,20 @@ static void init_boundary_distances(AvoidCrossingPerimeters::Boundary *boundary) precompute_polygon_distances(boundary->boundaries[poly_idx], boundary->boundaries_params[poly_idx]); } +static void init_boundary(AvoidCrossingPerimeters::Boundary *boundary, Polygons &&boundary_polygons) +{ + boundary->clear(); + boundary->boundaries = std::move(boundary_polygons); + + BoundingBox bbox(get_extents(boundary->boundaries)); + bbox.offset(SCALED_EPSILON); + boundary->bbox = BoundingBoxf(bbox.min.cast<double>(), bbox.max.cast<double>()); + boundary->grid.set_bbox(bbox); + // FIXME 1mm grid? + boundary->grid.create(boundary->boundaries, coord_t(scale_(1.))); + init_boundary_distances(boundary); +} + // Plan travel, which avoids perimeter crossings by following the boundaries of the layer. Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) { @@ -815,30 +875,29 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & if (!use_external && !gcodegen.layer()->lslices.empty() && !any_expolygon_contains(gcodegen.layer()->lslices, gcodegen.layer()->lslices_bboxes, m_grid_lslice, travel)) { // Initialize m_internal only when it is necessary. - if (m_internal.boundaries.empty()) { - m_internal.boundaries_params.clear(); - m_internal.boundaries = to_polygons(get_boundary(*gcodegen.layer())); - - BoundingBox bbox(get_extents(m_internal.boundaries)); - bbox.offset(SCALED_EPSILON); - m_internal.bbox = BoundingBoxf(bbox.min.cast<double>(), bbox.max.cast<double>()); - m_internal.grid.set_bbox(bbox); - // FIXME 1mm grid? - m_internal.grid.create(m_internal.boundaries, coord_t(scale_(1.))); - init_boundary_distances(&m_internal); - } + if (m_internal.boundaries.empty()) + init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer()))); // Trim the travel line by the bounding box. if (Geometry::liang_barsky_line_clipping(startf, endf, m_internal.bbox)) { travel_intersection_count = avoid_perimeters(gcodegen, m_internal, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl); result_pl.points.front() = start; result_pl.points.back() = end; - } else { - // Travel line is completely outside the bounding box. - result_pl = {start, end}; - travel_intersection_count = 0; } - } else { + } else if(use_external) { + // Initialize m_external only when exist any external travel for the current layer. + if (m_external.boundaries.empty()) + init_boundary(&m_external, get_boundary_external(*gcodegen.layer())); + + // Trim the travel line by the bounding box. + if (!m_external.boundaries.empty() && Geometry::liang_barsky_line_clipping(startf, endf, m_external.bbox)) { + travel_intersection_count = avoid_perimeters(gcodegen, m_external, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl); + result_pl.points.front() = start; + result_pl.points.back() = end; + } + } + + if(result_pl.empty()) { // Travel line is completely outside the bounding box. result_pl = {start, end}; travel_intersection_count = 0; @@ -861,8 +920,9 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & void AvoidCrossingPerimeters::init_layer(const Layer &layer) { - m_internal.boundaries.clear(); - m_internal.boundaries_params.clear(); + m_internal.clear(); + m_external.clear(); + BoundingBox bbox_slice(get_extents(layer.lslices)); bbox_slice.offset(SCALED_EPSILON); @@ -1120,29 +1180,6 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & return result_pl; } -// called by get_boundary_external() -static float get_perimeter_spacing_external(const Layer &layer) -{ - size_t regions_count = 0; - float perimeter_spacing = 0.f; - for (const PrintObject *object : layer.object()->print()->objects()) - //FIXME with different layering, layers on other objects will not be found at this object's print_z. - // Search an overlap of layers? - if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l) - for (const LayerRegion *layer_region : l->regions()) - if (layer_region != nullptr && !layer_region->slices.empty()) { - perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing(); - ++ regions_count; - } - - assert(perimeter_spacing >= 0.f); - if (regions_count != 0) - perimeter_spacing /= float(regions_count); - else - perimeter_spacing = get_default_perimeter_spacing(*layer.object()); - return perimeter_spacing; -} - // called by AvoidCrossingPerimeters::init_layer()->get_boundary()/get_boundary_external() static std::pair<Polygons, Polygons> split_expolygon(const ExPolygons &ex_polygons) { diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index 1ed1f0026..03c420a32 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -41,6 +41,12 @@ public: std::vector<std::vector<float>> boundaries_params; // Used for detection of intersection between line and any polygon from boundaries EdgeGrid::Grid grid; + + void clear() + { + boundaries.clear(); + boundaries_params.clear(); + } }; private: @@ -55,10 +61,8 @@ private: EdgeGrid::Grid m_grid_lslice; // Store all needed data for travels inside object Boundary m_internal; -#if 0 // Store all needed data for travels outside object Boundary m_external; -#endif }; } // namespace Slic3r