Fixes of recent RegionExpansion implementation.

Enabled thick internal bridges even if external thick bridges are disabled.
Fixed compilation of conditionally compiled debugging code.
This commit is contained in:
Vojtech Bubnik 2023-01-06 17:53:49 +01:00
parent 479a39ce0e
commit 1a91d85e7e
6 changed files with 53 additions and 92 deletions

View File

@ -76,12 +76,11 @@ RegionExpansionParameters RegionExpansionParameters::build(
// similar to expolygons_to_zpaths(), but each contour is expanded before converted to zpath.
// The expanded contours are then opened (the first point is repeated at the end).
static ClipperLib_Z::Paths expolygons_to_zpaths_expanded_opened(
const ExPolygons &src, const float expansion, coord_t base_idx)
const ExPolygons &src, const float expansion, coord_t &base_idx)
{
ClipperLib_Z::Paths out;
out.reserve(2 * std::accumulate(src.begin(), src.end(), size_t(0),
[](const size_t acc, const ExPolygon &expoly) { return acc + expoly.num_contours(); }));
coord_t z = base_idx;
ClipperLib::ClipperOffset offsetter;
offsetter.ShortestEdgeLength = expansion * ClipperOffsetShortestEdgeFactor;
ClipperLib::Paths expansion_cache;
@ -94,9 +93,9 @@ static ClipperLib_Z::Paths expolygons_to_zpaths_expanded_opened(
offsetter.AddPath(expoly.contour_or_hole(icontour).points, ClipperLib::jtSquare, ClipperLib::etClosedPolygon);
expansion_cache.clear();
offsetter.Execute(expansion_cache, icontour == 0 ? expansion : -expansion);
append(out, ClipperZUtils::to_zpaths<true>(expansion_cache, z));
append(out, ClipperZUtils::to_zpaths<true>(expansion_cache, base_idx));
}
++ z;
++ base_idx;
}
return out;
}
@ -217,7 +216,7 @@ std::vector<WaveSeed> wave_seeds(
Intersections intersections;
coord_t idx_boundary_begin = 1;
coord_t idx_boundary_end;
coord_t idx_boundary_end = idx_boundary_begin;
coord_t idx_src_end;
{
@ -225,17 +224,13 @@ std::vector<WaveSeed> wave_seeds(
ClipperZUtils::ClipperZIntersectionVisitor visitor(intersections);
zclipper.ZFillFunction(visitor.clipper_callback());
// as closed contours
{
ClipperLib_Z::Paths zboundary = ClipperZUtils::expolygons_to_zpaths(boundary, idx_boundary_begin);
idx_boundary_end = idx_boundary_begin + coord_t(zboundary.size());
zclipper.AddPaths(zboundary, ClipperLib_Z::ptClip, true);
}
zclipper.AddPaths(ClipperZUtils::expolygons_to_zpaths(boundary, idx_boundary_end), ClipperLib_Z::ptClip, true);
// as open contours
std::vector<std::pair<ClipperLib_Z::IntPoint, int>> zsrc_splits;
{
ClipperLib_Z::Paths zsrc = expolygons_to_zpaths_expanded_opened(src, tiny_expansion, idx_boundary_end);
idx_src_end = idx_boundary_end;
ClipperLib_Z::Paths zsrc = expolygons_to_zpaths_expanded_opened(src, tiny_expansion, idx_src_end);
zclipper.AddPaths(zsrc, ClipperLib_Z::ptSubject, false);
idx_src_end = idx_boundary_end + coord_t(zsrc.size());
zsrc_splits.reserve(zsrc.size());
for (const ClipperLib_Z::Path &path : zsrc) {
assert(path.size() >= 2);
@ -267,7 +262,10 @@ std::vector<WaveSeed> wave_seeds(
const ClipperLib_Z::IntPoint &back = path.back();
// Both ends of a seed segment are supposed to be inside a single boundary expolygon.
// Thus as long as the seed contour is not closed, it should be open at a boundary point.
assert((front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end) || (front.z() < 0 && back.z() < 0));
assert((front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end) ||
//(front.z() < 0 && back.z() < 0));
// Hope that at least one end of an open polyline is clipped by the boundary, thus an intersection point is created.
(front.z() < 0 || back.z() < 0));
const Intersection *intersection = nullptr;
auto intersection_point_valid = [idx_boundary_end, idx_src_end](const Intersection &is) {
return is.first >= 1 && is.first < idx_boundary_end &&

View File

@ -53,17 +53,16 @@ inline ZPaths to_zpaths(const std::vector<Points> &paths, coord_t z)
// offsetted by base_index.
// If Open, then duplicate the first point of each path at its end.
template<bool Open = false>
inline ZPaths expolygons_to_zpaths(const ExPolygons &src, coord_t base_idx)
inline ZPaths expolygons_to_zpaths(const ExPolygons &src, coord_t &base_idx)
{
ZPaths out;
out.reserve(std::accumulate(src.begin(), src.end(), size_t(0),
[](const size_t acc, const ExPolygon &expoly) { return acc + expoly.num_contours(); }));
coord_t z = base_idx;
for (const ExPolygon &expoly : src) {
out.emplace_back(to_zpath<Open>(expoly.contour.points, z));
out.emplace_back(to_zpath<Open>(expoly.contour.points, base_idx));
for (const Polygon &hole : expoly.holes)
out.emplace_back(to_zpath<Open>(hole.points, z));
++ z;
out.emplace_back(to_zpath<Open>(hole.points, base_idx));
++ base_idx;
}
return out;
}

View File

@ -215,19 +215,19 @@ Surfaces expand_bridges_detect_orientations(
// bridge_expansions are sorted by boundary id and source id.
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end();) {
// For each boundary region:
auto it2 = it;
for (++ it2; it2 != bridge_expansions.end() && it2->boundary_id == it->boundary_id; ++ it2);
auto it_begin = it;
auto it_end = std::next(it_begin);
for (; it_end != bridge_expansions.end() && it_end->boundary_id == it_begin->boundary_id; ++ it_end) ;
bboxes.clear();
bboxes.reserve(it2 - it);
for (it2 = it; it2 != bridge_expansions.end() && it2->boundary_id == it->boundary_id; ++ it2)
bboxes.reserve(it_end - it_begin);
for (auto it2 = it_begin; it2 != it_end; ++ it2)
bboxes.emplace_back(get_extents(it2->expolygon.contour));
auto it_end = it2;
// For each bridge anchor of the current source:
for (; it != it_end; ++ it) {
// A grup id for this bridge.
for (it2 = std::next(it); it2 != it_end; ++ it2)
for (auto it2 = std::next(it); it2 != it_end; ++ it2)
if (it->src_id != it2->src_id &&
bboxes[it - bridge_expansions.begin()].overlap(bboxes[it2 - bridge_expansions.begin()]) &&
bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) &&
// One may ignore holes, they are irrelevant for intersection test.
! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) {
// The two bridge regions intersect. Give them the same group id.

View File

@ -964,9 +964,9 @@ void PrintObject::detect_surfaces_type()
{
static int iRun = 0;
std::vector<std::pair<Slic3r::ExPolygons, SVG::ExPolygonAttributes>> expolygons_with_attributes;
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black")));
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices().surfaces), SVG::ExPolygonAttributes("black")));
SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes);
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@ -1399,26 +1399,26 @@ void PrintObject::discover_vertical_shells()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternal), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternal), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalsolid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternalSolid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternalSolid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@ -1544,7 +1544,7 @@ void PrintObject::bridge_over_infill()
internals.reserve(this->layer_count());
for (Layer *layer : m_layers) {
Polygons sum;
for (const LayerRegion *layerm : layer->m_regions)
for (const LayerRegion *layerm : layer->regions())
layerm->fill_surfaces().filter_by_type(stInternal, &sum);
internals.emplace_back(std::move(sum));
}
@ -1558,7 +1558,7 @@ void PrintObject::bridge_over_infill()
const size_t region_id = sparse_infill_regions[task_id % sparse_infill_regions.size()];
Layer *layer = this->get_layer(layer_id);
LayerRegion *layerm = layer->m_regions[region_id];
Flow bridge_flow = layerm->bridging_flow(frSolidInfill);
Flow bridge_flow = layerm->bridging_flow(frSolidInfill, true /* Internal bridges are always thick. */);
// Extract the stInternalSolid surfaces that might be transformed into bridges.
ExPolygons internal_solid;
@ -1567,32 +1567,27 @@ void PrintObject::bridge_over_infill()
// No internal solid -> no new bridges for this layer region.
continue;
// check whether the lower area is deep enough for absorbing the extra flow
// (for obvious physical reasons but also for preventing the bridge extrudates
// from overflowing in 3D preview)
// Check whether the lower area is deep enough for absorbing the extra flow, also filter out
// tiny regions from bridging.
ExPolygons to_bridge;
{
Polygons to_bridge_pp = to_polygons(internal_solid);
// Iterate through lower layers spanned by bridge_flow.
double bottom_z = layer->print_z - bridge_flow.height() - EPSILON;
for (auto i = int(layer_id) - 1; i >= 0; -- i) {
// Stop iterating if layer is lower than bottom_z.
if (m_layers[i]->print_z < bottom_z)
break;
for (auto i = int(layer_id) - 1; i >= 0 && m_layers[i]->print_z > bottom_z; -- i)
// Intersect lower sparse infills with the candidate solid surfaces.
to_bridge_pp = intersection(to_bridge_pp, internals[i]);
}
// there's no point in bridging too thin/short regions
//FIXME Vojtech: The offset2 function is not a geometric offset,
// therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour.
// The gaps will be filled by a separate region, which makes the infill less stable and it takes longer.
{
float min_width = float(bridge_flow.scaled_width()) * 3.f;
to_bridge_pp = opening(to_bridge_pp, min_width);
to_bridge_pp = opening(to_bridge_pp, min_width); //, ClipperLib::jtSquare);
}
if (to_bridge_pp.empty()) {
// Restore internal_solid surfaces.
// Optimization: Nothing to bridge, restore internal_solid surfaces.
for (ExPolygon &ex : internal_solid)
layerm->m_fill_surfaces.surfaces.push_back(Surface(stInternalSolid, std::move(ex)));
continue;
@ -1613,39 +1608,6 @@ void PrintObject::bridge_over_infill()
layerm->m_fill_surfaces.surfaces.push_back(Surface(stInternalBridge, std::move(ex)));
for (ExPolygon &ex : not_to_bridge)
layerm->m_fill_surfaces.surfaces.push_back(Surface(stInternalSolid, std::move(ex)));
/*
# exclude infill from the layers below if needed
# see discussion at https://github.com/alexrj/Slic3r/issues/240
# Update: do not exclude any infill. Sparse infill is able to absorb the excess material.
if (0) {
my $excess = $layerm->extruders->{infill}->bridge_flow->width - $layerm->height;
for (my $i = $layer_id-1; $excess >= $self->get_layer($i)->height; $i--) {
Slic3r::debugf " skipping infill below those areas at layer %d\n", $i;
foreach my $lower_layerm (@{$self->get_layer($i)->regions}) {
my @new_surfaces = ();
# subtract the area from all types of surfaces
foreach my $group (@{$lower_layerm->fill_surfaces->group}) {
push @new_surfaces, map $group->[0]->clone(expolygon => $_),
@{diff_ex(
[ map $_->p, @$group ],
[ map @$_, @$to_bridge ],
)};
push @new_surfaces, map Slic3r::Surface->new(
expolygon => $_,
surface_type => stInternalVoid,
), @{intersection_ex(
[ map $_->p, @$group ],
[ map @$_, @$to_bridge ],
)};
}
$lower_layerm->fill_surfaces->clear;
$lower_layerm->fill_surfaces->append($_) for @new_surfaces;
}
$excess -= $self->get_layer($i)->height;
}
}
*/
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("7_bridge_over_infill");
layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill");

View File

@ -1320,7 +1320,7 @@ namespace SupportMaterialInternal {
bridges = union_(bridges);
}
// remove the entire bridges and only support the unsupported edges
//FIXME the brided regions are already collected as layerm.bridged. Use it?
//FIXME the bridged regions are already collected as layerm.bridged. Use it?
for (const Surface &surface : layerm.fill_surfaces())
if (surface.surface_type == stBottomBridge && surface.bridge_angle < 0.0)
polygons_append(bridges, surface.expolygon);

View File

@ -62,6 +62,8 @@ public:
// Inherits coord_t x, y
};
#define DEBUG_INTERSECTIONLINE (! defined(NDEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING))
class IntersectionLine : public Line
{
public:
@ -119,14 +121,14 @@ public:
};
uint32_t flags { 0 };
#ifndef NDEBUG
#if DEBUG_INTERSECTIONLINE
enum class Source {
BottomPlane,
TopPlane,
Slab,
};
Source source { Source::BottomPlane };
#endif // NDEBUG
#endif
};
using IntersectionLines = std::vector<IntersectionLine>;
@ -1440,24 +1442,24 @@ static std::vector<Polygons> make_slab_loops(
for (const IntersectionLine &l : lines.at_slice[slice_below])
if (l.edge_type != IntersectionLine::FacetEdgeType::Top) {
in.emplace_back(l);
#ifndef NDEBUG
#if DEBUG_INTERSECTIONLINE
in.back().source = IntersectionLine::Source::BottomPlane;
#endif // NDEBUG
#endif // DEBUG_INTERSECTIONLINE
}
}
{
// Edges in between slice_below and slice_above.
#ifndef NDEBUG
#if DEBUG_INTERSECTIONLINE
size_t old_size = in.size();
#endif // NDEBUG
#endif // DEBUG_INTERSECTIONLINE
// Edge IDs of end points on in-between lines that touch the layer above are already increased with num_edges.
append(in, lines.between_slices[line_idx]);
#ifndef NDEBUG
#if DEBUG_INTERSECTIONLINE
for (auto it = in.begin() + old_size; it != in.end(); ++ it) {
assert(it->edge_type == IntersectionLine::FacetEdgeType::Slab);
it->source = IntersectionLine::Source::Slab;
}
#endif // NDEBUG
#endif // DEBUG_INTERSECTIONLINE
}
if (has_slice_above) {
for (const IntersectionLine &lsrc : lines.at_slice[slice_above])
@ -1470,9 +1472,9 @@ static std::vector<Polygons> make_slab_loops(
l.edge_a_id += num_edges;
if (l.edge_b_id >= 0)
l.edge_b_id += num_edges;
#ifndef NDEBUG
#if DEBUG_INTERSECTIONLINE
l.source = IntersectionLine::Source::TopPlane;
#endif // NDEBUG
#endif // DEBUG_INTERSECTIONLINE
}
}
if (! in.empty()) {