Skip to content

Commit

Permalink
extra_perimeters: now also add extra perimeters if infill-over-overha…
Browse files Browse the repository at this point in the history
…ngs are detected.

extra_perimeters: Use CW instead of CCW every odd layers for overhangs.
  • Loading branch information
supermerill committed Jan 25, 2019
1 parent a4a0d52 commit 63721fb
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 32 deletions.
60 changes: 42 additions & 18 deletions xs/src/libslic3r/BridgeDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,11 @@ BridgeDetector::detect_angle()
}

Polygons
BridgeDetector::coverage() const
{
if (this->angle == -1) return Polygons();
return this->coverage(this->angle);
}

Polygons
BridgeDetector::coverage(double angle) const
BridgeDetector::coverage(double angle, bool precise) const
{
if (angle == -1) angle = this->angle;
if (angle == -1) return Polygons();

// Clone our expolygon and rotate it so that we work with vertical lines.
ExPolygon expolygon = this->expolygon;
expolygon.rotate(PI/2.0 - angle, Point(0,0));
Expand All @@ -214,18 +210,46 @@ BridgeDetector::coverage(double angle) const
}

Polygons covered;
for (const Polygon &trapezoid : trapezoids) {
Lines supported = intersection_ln(trapezoid.lines(), anchors);
for (Polygon &trapezoid : trapezoids) {

// not nice, we need a more robust non-numeric check
for (size_t i = 0; i < supported.size(); ++i) {
if (supported[i].length() < this->extrusion_width) {
supported.erase(supported.begin() + i);
i--;
}
}
size_t n_supported = 0;
if (!precise) {
// not nice, we need a more robust non-numeric check
for (const Line &supported_line : intersection_ln(trapezoid.lines(), anchors))
if (supported_line.length() >= this->extrusion_width)
++n_supported;
} else {
Polygons intersects = intersection(trapezoid, anchors);
n_supported = intersects.size();

if (n_supported >= 2) {
// trim it to not allow to go outside of the intersections
BoundingBox center_bound = intersects[0].bounding_box();
coord_t min_y = center_bound.center().y, max_y = center_bound.center().y;
for (const Polygon &poly_bound : intersects) {
center_bound = poly_bound.bounding_box();
if (min_y > center_bound.center().y) min_y = center_bound.center().y;
if (max_y < center_bound.center().y) max_y = center_bound.center().y;
}
coord_t min_x = trapezoid[0].x, max_x = trapezoid[0].x;
for (const Point &p : trapezoid.points) {
if (min_x > p.x) min_x = p.x;
if (max_x < p.x) max_x = p.x;
}
//add what get_trapezoids3 has removed (+EPSILON)
min_x -= (this->extrusion_width / 4 + 1);
max_x += (this->extrusion_width / 4 + 1);
coord_t mid_x = (min_x + max_x) / 2;
for (Point &p : trapezoid.points) {
if (p.y < min_y) p.y = min_y;
if (p.y > max_y) p.y = max_y;
if (p.x > min_x && p.x < mid_x) p.x = min_x;
if (p.x < max_x && p.x > mid_x) p.x = max_x;
}
}
}

if (supported.size() >= 2) covered.push_back(trapezoid);
if (n_supported >= 2) covered.push_back(trapezoid);
}

// merge trapezoids and rotate them back
Expand Down
3 changes: 1 addition & 2 deletions xs/src/libslic3r/BridgeDetector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ class BridgeDetector {

BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
bool detect_angle();
Polygons coverage() const;
Polygons coverage(double angle) const;
Polygons coverage(double angle = -1, bool precise = false) const;

/// Return the bridge edges that are not currently supported but would permit use of the supplied
/// bridge angle if it was supported.
Expand Down
3 changes: 2 additions & 1 deletion xs/src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,8 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
// next copies (if any) would not detect the correct orientation

// extrude all loops ccw
bool was_clockwise = loop.make_counter_clockwise();
// no! this was decided by the generator, he know better than us.
bool was_clockwise = false;// loop.make_counter_clockwise();

SeamPosition seam_position = this->config.seam_position;
if (loop.role == elrSkirt) seam_position = spNearest;
Expand Down
92 changes: 81 additions & 11 deletions xs/src/libslic3r/PerimeterGenerator.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "PerimeterGenerator.hpp"
#include "ClipperUtils.hpp"
#include "BridgeDetector.hpp"
#include "ExtrusionEntityCollection.hpp"
#include <cmath>
#include <cassert>
Expand Down Expand Up @@ -69,7 +70,7 @@ PerimeterGenerator::process()
loops = min_loops;
}

const int loop_number = loops-1; // 0-indexed loops
int loop_number = loops-1; // 0-indexed loops


Polygons gaps;
Expand All @@ -80,6 +81,48 @@ PerimeterGenerator::process()
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
std::vector<PerimeterGeneratorLoops> holes(loop_number+1); // depth => loops
ThickPolylines thin_walls;


// We can add more perimeters if there are uncovered overhangs
bool has_overhang = false;
if (this->config->extra_perimeters && !last.empty()
&& this->lower_slices != NULL && !this->lower_slices->expolygons.empty()){
//split the polygons with bottom/notbottom
ExPolygons unsupported = diff_ex(last, to_polygons(this->lower_slices->expolygons), true);
if (!unsupported.empty()) {
//only consider overhangs and let bridges alone

//first, separate into islands (ie, each ExPlolygon)
int numploy = 0;
//only consider the bottom layer that intersect unsupported, to be sure it's only on our island.
ExPolygonCollection lower_island(diff_ex(last, to_polygons(unsupported), true));
BridgeDetector detector(unsupported,
lower_island,
pspacing);
if (detector.detect_angle()) {
ExPolygons bridgeable = union_ex(detector.coverage(-1, true));
if (!bridgeable.empty()) {
//simplify to avoid most of artefacts from printing lines.
ExPolygons bridgeable_simplified;
for (ExPolygon &poly : bridgeable) {
poly.simplify(pspacing / 2, &bridgeable_simplified);
}

if (!bridgeable_simplified.empty())
bridgeable_simplified = offset_ex(bridgeable_simplified, pspacing/1.9);
if (!bridgeable_simplified.empty()) {
//offset by perimeter spacing because the simplify may have reduced it a bit.
unsupported = diff_ex(unsupported, bridgeable_simplified, true);
}
}
}
if (!unsupported.empty()) {
//allow to add an other perimeter
has_overhang = true;
}
}
}


// we loop one time more than needed in order to find gaps after the last perimeter was applied
for (int i = 0; i <= loop_number+1; ++i) { // outer loop is 0
Expand Down Expand Up @@ -181,19 +224,43 @@ PerimeterGenerator::process()
}
}

if (offsets.empty()) break;
if (i > loop_number) break; // we were only looking for gaps this time
// if (offsets.empty()) break;
// if (i > loop_number) break; // we were only looking for gaps this time

last = offsets;
for (Polygons::const_iterator polygon = offsets.begin(); polygon != offsets.end(); ++polygon) {
PerimeterGeneratorLoop loop(*polygon, i);
loop.is_contour = polygon->is_counter_clockwise();
if (loop.is_contour) {
contours[i].push_back(loop);
if (offsets.empty()) {
// Store the number of loops actually generated.
loop_number = i - 1;
break;
} else if (i > loop_number) {
if (has_overhang) {
loop_number++;
contours.emplace_back();
holes.emplace_back();
} else {
holes[i].push_back(loop);
// If i > loop_number, we were looking just for gaps.
break;
}
}

for (const Polygon &polygon : offsets) {
if(polygon.is_counter_clockwise()){
contours[i].emplace_back(PerimeterGeneratorLoop(polygon, i, true, has_overhang));
}else{
holes[i].emplace_back(PerimeterGeneratorLoop(polygon, i, false, has_overhang));
}
}
last = std::move(offsets);

// last = offsets;
// for (Polygons::const_iterator polygon = offsets.begin(); polygon != offsets.end(); ++polygon) {
// PerimeterGeneratorLoop loop(*polygon, i);
// loop.is_contour = polygon->is_counter_clockwise();
// if (loop.is_contour) {
// contours[i].push_back(loop);
// } else {
// holes[i].push_back(loop);
// }
// }
}

// nest loops: holes first
Expand Down Expand Up @@ -450,7 +517,10 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,

ExtrusionEntityCollection children = this->_traverse_loops(loop.children, thin_walls);
if (loop.is_contour) {
eloop.make_counter_clockwise();
if (loop.is_overhang && this->layer_id % 2 == 1)
eloop.make_clockwise();
else
eloop.make_counter_clockwise();
entities.append(children.entities);
entities.append(eloop);
} else {
Expand Down
6 changes: 6 additions & 0 deletions xs/src/libslic3r/PerimeterGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ class PerimeterGeneratorLoop {
// Is it a contour or a hole?
// Contours are CCW oriented, holes are CW oriented.
bool is_contour;
//overhang may need to be reversed
bool is_overhang;
// Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole.
unsigned short depth;
// Children contour, may be both CCW and CW oriented (outer contours or holes).
std::vector<PerimeterGeneratorLoop> children;

PerimeterGeneratorLoop(Polygon polygon, unsigned short depth, bool is_contour) :
polygon(polygon), is_contour(is_contour), depth(depth), is_overhang(false) {}
PerimeterGeneratorLoop(Polygon polygon, unsigned short depth, bool is_contour, bool is_overhang) :
polygon(polygon), is_contour(is_contour), depth(depth), is_overhang(is_overhang) {}
PerimeterGeneratorLoop(Polygon polygon, unsigned short depth)
: polygon(polygon), is_contour(false), depth(depth)
{};
Expand Down

0 comments on commit 63721fb

Please sign in to comment.