Fixed an issue with a gap in brim when inner brim was used.

This gap still can appear for holes that contain another object.
Also fixed an issue that a huge outer brim can overflow into holes of another object.
This commit is contained in:
Lukáš Hejl 2022-02-07 10:19:02 +01:00
parent 874da0281d
commit e20d041fc9

View File

@ -10,7 +10,10 @@
#include <algorithm> #include <algorithm>
#include <numeric> #include <numeric>
#include <unordered_set> #include <unordered_set>
#include <mutex>
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <boost/thread/lock_guard.hpp>
#ifndef NDEBUG #ifndef NDEBUG
// #define BRIM_DEBUG_TO_SVG // #define BRIM_DEBUG_TO_SVG
@ -200,20 +203,95 @@ static ExPolygons top_level_outer_brim_area(const Print &print
return diff_ex(brim_area, no_brim_area); return diff_ex(brim_area, no_brim_area);
} }
static ExPolygons inner_brim_area(const Print &print, // Return vector of booleans indicated if polygons from bottom_layers_expolygons contain another polygon or not.
const ConstPrintObjectPtrs &top_level_objects_with_brim, // Every ExPolygon is counted as several Polygons (contour and holes). Contour polygon is always processed before holes.
const std::vector<ExPolygons> &bottom_layers_expolygons, static std::vector<bool> has_polygons_nothing_inside(const Print &print, const std::vector<ExPolygons> &bottom_layers_expolygons)
const float no_brim_offset)
{ {
assert(print.objects().size() == bottom_layers_expolygons.size()); assert(print.objects().size() == bottom_layers_expolygons.size());
Polygons islands;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx];
const Polygons islands_object = to_polygons(bottom_layers_expolygons[print_object_idx]);
islands.reserve(islands.size() + object->instances().size() * islands_object.size());
for (const PrintInstance &instance : object->instances())
append_and_translate(islands, islands_object, instance);
}
ClipperLib_Z::Paths islands_clip;
islands_clip.reserve(islands.size());
for (const Polygon &poly : islands) {
size_t island_idx = &poly - &islands.front();
ClipperLib_Z::Path island_clip;
for (const Point &pt : poly.points)
island_clip.emplace_back(pt.x(), pt.y(), island_idx + 1);
islands_clip.emplace_back(island_clip);
}
ClipperLib_Z::Clipper clipper;
// Assign the maximum Z from four points. This values is valid index of the island
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot,
const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
});
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
ClipperLib_Z::PolyTree islands_polytree;
clipper.Execute(ClipperLib_Z::ctUnion, islands_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
std::vector<bool> has_nothing_inside(islands.size());
std::function<void(const ClipperLib_Z::PolyNode&)> check_contours = [&check_contours, &has_nothing_inside](const ClipperLib_Z::PolyNode &parent_node)->void {
if (!parent_node.Childs.empty())
for(const ClipperLib_Z::PolyNode *child_node : parent_node.Childs)
check_contours(*child_node);
if (parent_node.Childs.empty() && !parent_node.Contour.empty() && parent_node.Contour.front().z() != 0) {
int polygon_idx = parent_node.Contour.front().z();
assert(polygon_idx > 0 && polygon_idx <= has_nothing_inside.size());
// The whole contour must have the same ID. In other cases, some counters overlap.
for (const ClipperLib_Z::IntPoint &point : parent_node.Contour)
if (polygon_idx != point.z())
return;
has_nothing_inside[polygon_idx - 1] = true;
}
};
check_contours(islands_polytree);
return has_nothing_inside;
}
// INNERMOST means that ExPolygon doesn't contain any other ExPolygons.
// NORMAL is for other cases.
enum class InnerBrimType {NORMAL, INNERMOST};
struct InnerBrimExPolygons
{
ExPolygons brim_area;
InnerBrimType type = InnerBrimType::NORMAL;
double brim_width = 0.;
};
static std::vector<InnerBrimExPolygons> inner_brim_area(const Print &print,
const ConstPrintObjectPtrs &top_level_objects_with_brim,
const std::vector<ExPolygons> &bottom_layers_expolygons,
const float no_brim_offset)
{
assert(print.objects().size() == bottom_layers_expolygons.size());
std::vector<bool> has_nothing_inside = has_polygons_nothing_inside(print, bottom_layers_expolygons);
std::unordered_set<size_t> top_level_objects_idx; std::unordered_set<size_t> top_level_objects_idx;
top_level_objects_idx.reserve(top_level_objects_with_brim.size()); top_level_objects_idx.reserve(top_level_objects_with_brim.size());
for (const PrintObject *object : top_level_objects_with_brim) for (const PrintObject *object : top_level_objects_with_brim)
top_level_objects_idx.insert(object->id().id); top_level_objects_idx.insert(object->id().id);
ExPolygons brim_area; std::vector<ExPolygons> brim_area_innermost(print.objects().size());
ExPolygons no_brim_area; ExPolygons brim_area;
Polygons holes; ExPolygons no_brim_area;
Polygons holes;
// polygon_idx must correspond to idx generated inside has_polygons_nothing_inside()
size_t polygon_idx = 0;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) { for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx]; const PrintObject *object = print.objects()[print_object_idx];
const BrimType brim_type = object->config().brim_type.value; const BrimType brim_type = object->config().brim_type.value;
@ -221,10 +299,13 @@ static ExPolygons inner_brim_area(const Print &print,
const float brim_width = scale_(object->config().brim_width.value); const float brim_width = scale_(object->config().brim_width.value);
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
ExPolygons brim_area_innermost_object;
ExPolygons brim_area_object; ExPolygons brim_area_object;
ExPolygons no_brim_area_object; ExPolygons no_brim_area_object;
Polygons holes_object; Polygons holes_object;
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) { for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
++polygon_idx; // Increase idx because of the contour of the ExPolygon.
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) { if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) {
if (top_outer_brim) if (top_outer_brim)
no_brim_area_object.emplace_back(ex_poly); no_brim_area_object.emplace_back(ex_poly);
@ -236,7 +317,13 @@ static ExPolygons inner_brim_area(const Print &print,
Polygons ex_poly_holes_reversed = ex_poly.holes; Polygons ex_poly_holes_reversed = ex_poly.holes;
polygons_reverse(ex_poly_holes_reversed); polygons_reverse(ex_poly_holes_reversed);
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
append(brim_area_object, diff_ex(shrink_ex(ex_poly_holes_reversed, brim_separation, ClipperLib::jtSquare), shrink_ex(ex_poly_holes_reversed, brim_width + brim_separation, ClipperLib::jtSquare))); for(const Polygon &hole : ex_poly_holes_reversed) {
size_t hole_idx = &hole - &ex_poly_holes_reversed.front();
if (has_nothing_inside[polygon_idx + hole_idx])
append(brim_area_innermost_object, shrink_ex({hole}, brim_separation, ClipperLib::jtSquare));
else
append(brim_area_object, diff_ex(shrink_ex({hole}, brim_separation, ClipperLib::jtSquare), shrink_ex({hole}, brim_width + brim_separation, ClipperLib::jtSquare)));
}
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly_holes_reversed)); append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly_holes_reversed));
@ -245,17 +332,34 @@ static ExPolygons inner_brim_area(const Print &print,
append(no_brim_area_object, diff_ex(ExPolygon(ex_poly.contour), shrink_ex(ex_poly_holes_reversed, no_brim_offset, ClipperLib::jtSquare))); append(no_brim_area_object, diff_ex(ExPolygon(ex_poly.contour), shrink_ex(ex_poly_holes_reversed, no_brim_offset, ClipperLib::jtSquare)));
append(holes_object, ex_poly_holes_reversed); append(holes_object, ex_poly_holes_reversed);
polygon_idx += ex_poly.holes.size(); // Increase idx for every hole of the ExPolygon.
} }
append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_separation, ClipperLib::jtSquare)); append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_separation, ClipperLib::jtSquare));
for (const PrintInstance &instance : object->instances()) { for (const PrintInstance &instance : object->instances()) {
append_and_translate(brim_area_innermost[print_object_idx], brim_area_innermost_object, instance);
append_and_translate(brim_area, brim_area_object, instance); append_and_translate(brim_area, brim_area_object, instance);
append_and_translate(no_brim_area, no_brim_area_object, instance); append_and_translate(no_brim_area, no_brim_area_object, instance);
append_and_translate(holes, holes_object, instance); append_and_translate(holes, holes_object, instance);
} }
} }
assert(polygon_idx == has_nothing_inside.size());
return diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes), no_brim_area); ExPolygons brim_area_innermost_merged;
// Append all innermost brim areas.
std::vector<InnerBrimExPolygons> brim_area_out;
for (size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx)
if (const double brim_width = print.objects()[print_object_idx]->config().brim_width.value; !brim_area_innermost[print_object_idx].empty()) {
append(brim_area_innermost_merged, brim_area_innermost[print_object_idx]);
brim_area_out.push_back({std::move(brim_area_innermost[print_object_idx]), InnerBrimType::INNERMOST, brim_width});
}
// Append all normal brim areas.
brim_area_out.push_back({diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes), no_brim_area), InnerBrimType::NORMAL});
// Cut out a huge brim areas that overflows into the INNERMOST holes.
brim_area_out.back().brim_area = diff_ex(brim_area_out.back().brim_area, brim_area_innermost_merged);
return brim_area_out;
} }
// Flip orientation of open polylines to minimize travel distance. // Flip orientation of open polylines to minimize travel distance.
@ -359,17 +463,28 @@ static void make_inner_brim(const Print &print,
ExtrusionEntityCollection &brim) ExtrusionEntityCollection &brim)
{ {
assert(print.objects().size() == bottom_layers_expolygons.size()); assert(print.objects().size() == bottom_layers_expolygons.size());
const auto scaled_resolution = scaled<double>(print.config().gcode_resolution.value); const auto scaled_resolution = scaled<double>(print.config().gcode_resolution.value);
Flow flow = print.brim_flow(); Flow flow = print.brim_flow();
ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing())); std::vector<InnerBrimExPolygons> inner_brims_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
Polygons loops; Polygons loops;
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), ClipperLib::jtSquare); std::mutex loops_mutex;
for (size_t i = 0; !islands_ex.empty(); ++i) { tbb::parallel_for(tbb::blocked_range<size_t>(0, inner_brims_ex.size()), [&inner_brims_ex, &flow, &scaled_resolution, &loops, &loops_mutex](const tbb::blocked_range<size_t> &range) {
for (ExPolygon &poly_ex : islands_ex) for (size_t brim_idx = range.begin(); brim_idx < range.end(); ++brim_idx) {
poly_ex.douglas_peucker(scaled_resolution); const InnerBrimExPolygons &inner_brim_ex = inner_brims_ex[brim_idx];
polygons_append(loops, to_polygons(islands_ex)); auto num_loops = size_t(floor(inner_brim_ex.brim_width / flow.spacing()));
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), ClipperLib::jtSquare); ExPolygons islands_ex = offset_ex(inner_brim_ex.brim_area, -0.5f * float(flow.scaled_spacing()), ClipperLib::jtSquare);
} for (size_t i = 0; (inner_brim_ex.type == InnerBrimType::INNERMOST ? i < num_loops : !islands_ex.empty()); ++i) {
for (ExPolygon &poly_ex : islands_ex)
poly_ex.douglas_peucker(scaled_resolution);
{
boost::lock_guard<std::mutex> lock(loops_mutex);
polygons_append(loops, to_polygons(islands_ex));
}
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), ClipperLib::jtSquare);
}
}
}); // end of parallel_for
loops = union_pt_chained_outside_in(loops); loops = union_pt_chained_outside_in(loops);
std::reverse(loops.begin(), loops.end()); std::reverse(loops.begin(), loops.end());