Merge branch 'master' into fs_emboss
# Conflicts: # src/slic3r/GUI/GLCanvas3D.cpp
This commit is contained in:
commit
7c1a513e5a
16 changed files with 924 additions and 414 deletions
|
@ -25,6 +25,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// execute fn for each pixel on the line. If fn returns false, terminate the iteration
|
||||
template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
|
||||
{
|
||||
coord_t dx = abs(x1 - x0);
|
||||
|
@ -39,7 +40,7 @@ template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t
|
|||
dy *= 2;
|
||||
|
||||
for (; n > 0; --n) {
|
||||
fn(x, y);
|
||||
if (!fn(x, y)) return;
|
||||
|
||||
if (error > 0) {
|
||||
x += x_inc;
|
||||
|
@ -55,11 +56,11 @@ template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t
|
|||
// may call the fn on the same coordiantes multiple times!
|
||||
template<typename PointFn> void double_dda_with_offset(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
|
||||
{
|
||||
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
|
||||
normal.x() = ceil(normal.x());
|
||||
normal.y() = ceil(normal.y());
|
||||
Point start_offset = Point(x0,y0) + (normal).cast<coord_t>();
|
||||
Point end_offset = Point(x1,y1) + (normal).cast<coord_t>();
|
||||
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
|
||||
normal.x() = ceil(normal.x());
|
||||
normal.y() = ceil(normal.y());
|
||||
Point start_offset = Point(x0, y0) + (normal).cast<coord_t>();
|
||||
Point end_offset = Point(x1, y1) + (normal).cast<coord_t>();
|
||||
|
||||
dda(x0, y0, x1, y1, fn);
|
||||
dda(start_offset.x(), start_offset.y(), end_offset.x(), end_offset.y(), fn);
|
||||
|
@ -95,7 +96,7 @@ private:
|
|||
|
||||
bool is_jump_point(CellPositionType pos, CellPositionType forward_dir) const
|
||||
{
|
||||
if (abs(forward_dir.x()) + abs(forward_dir.y()) == 2) {
|
||||
if (abs(forward_dir.x()) + abs(forward_dir.y()) == 2) {
|
||||
// diagonal
|
||||
CellPositionType horizontal_check_dir = CellPositionType{forward_dir.x(), 0};
|
||||
CellPositionType vertical_check_dir = CellPositionType{0, forward_dir.y()};
|
||||
|
@ -169,8 +170,11 @@ public:
|
|||
|
||||
float goal_heuristic(Node n) const { return n.position == target ? -1.f : (target - n.position).template cast<double>().norm(); }
|
||||
|
||||
size_t unique_id(Node n) const { return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y())); }
|
||||
|
||||
size_t unique_id(Node n) const
|
||||
{
|
||||
return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y()));
|
||||
}
|
||||
|
||||
const std::vector<CellPositionType> all_directions{{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}};
|
||||
};
|
||||
|
||||
|
@ -189,6 +193,7 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
|
|||
obstacle_min.x() = std::min(obstacle_min.x(), x);
|
||||
obstacle_min.y() = std::min(obstacle_min.y(), y);
|
||||
inpassable.insert(Pixel{x, y});
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const Line &l : obstacles) {
|
||||
|
@ -200,36 +205,13 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
|
|||
|
||||
void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin)
|
||||
{
|
||||
if (layer != nullptr) { this->print_z = layer->print_z; }
|
||||
if (layer == nullptr) return;
|
||||
|
||||
auto store_obstacle = [&](coord_t x, coord_t y) {
|
||||
obstacle_max.x() = std::max(obstacle_max.x(), x);
|
||||
obstacle_max.y() = std::max(obstacle_max.y(), y);
|
||||
obstacle_min.x() = std::min(obstacle_min.x(), x);
|
||||
obstacle_min.y() = std::min(obstacle_min.y(), y);
|
||||
inpassable.insert(Pixel{x, y});
|
||||
};
|
||||
this->print_z = layer->print_z;
|
||||
Lines obstacles;
|
||||
for (size_t step = 0; step < 3; step++) {
|
||||
if (layer != nullptr) {
|
||||
obstacles.insert(obstacles.end(), layer->malformed_lines.begin(), layer->malformed_lines.end());
|
||||
layer = layer->lower_layer;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Line &l : obstacles) {
|
||||
Pixel start = pixelize(l.a + global_origin);
|
||||
Pixel end = pixelize(l.b + global_origin);
|
||||
double_dda_with_offset(start.x(), start.y(), end.x(), end.y(), store_obstacle);
|
||||
}
|
||||
#ifdef DEBUG_FILES
|
||||
::Slic3r::SVG svg(debug_out_path(("obstacles_jps" + std::to_string(print_z) + "_" + std::to_string(rand() % 1000)).c_str()).c_str(),
|
||||
get_extents(obstacles));
|
||||
svg.draw(obstacles);
|
||||
svg.Close();
|
||||
#endif
|
||||
obstacles.reserve(layer->malformed_lines.size());
|
||||
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
add_obstacles(obstacles);
|
||||
}
|
||||
|
||||
Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
||||
|
@ -238,21 +220,40 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
|||
Pixel end = pixelize(p1);
|
||||
if (inpassable.empty() || (start - end).cast<float>().norm() < 3.0) { return Polyline{p0, p1}; }
|
||||
|
||||
BoundingBox search_box({start,end,obstacle_max,obstacle_min});
|
||||
search_box.max += Pixel(1,1);
|
||||
search_box.min -= Pixel(1,1);
|
||||
if (inpassable.find(start) != inpassable.end()) {
|
||||
dda(start.x(), start.y(), end.x(), end.y(), [&](coord_t x, coord_t y) {
|
||||
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
|
||||
start = Pixel(x, y);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (inpassable.find(end) != inpassable.end()) {
|
||||
dda(end.x(), end.y(), start.x(), start.y(), [&](coord_t x, coord_t y) {
|
||||
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
|
||||
end = Pixel(x, y);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
BoundingBox bounding_square(Points{start,end});
|
||||
bounding_square.max += Pixel(5,5);
|
||||
bounding_square.min -= Pixel(5,5);
|
||||
coord_t bounding_square_size = 2*std::max(bounding_square.size().x(),bounding_square.size().y());
|
||||
BoundingBox search_box({start, end, obstacle_max, obstacle_min});
|
||||
search_box.max += Pixel(1, 1);
|
||||
search_box.min -= Pixel(1, 1);
|
||||
|
||||
BoundingBox bounding_square(Points{start, end});
|
||||
bounding_square.max += Pixel(5, 5);
|
||||
bounding_square.min -= Pixel(5, 5);
|
||||
coord_t bounding_square_size = 2 * std::max(bounding_square.size().x(), bounding_square.size().y());
|
||||
bounding_square.max.x() += (bounding_square_size - bounding_square.size().x()) / 2;
|
||||
bounding_square.min.x() -= (bounding_square_size - bounding_square.size().x()) / 2;
|
||||
bounding_square.max.y() += (bounding_square_size - bounding_square.size().y()) / 2;
|
||||
bounding_square.min.y() -= (bounding_square_size - bounding_square.size().y()) / 2;
|
||||
|
||||
// Intersection - limit the search box to a square area around the start and end, to fasten the path searching
|
||||
// Intersection - limit the search box to a square area around the start and end, to fasten the path searching
|
||||
search_box.max = search_box.max.cwiseMin(bounding_square.max);
|
||||
search_box.min = search_box.min.cwiseMax(bounding_square.min);
|
||||
|
||||
|
@ -283,9 +284,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
|||
closest_qnode = astar_cache[closest_qnode].parent;
|
||||
}
|
||||
} else {
|
||||
for (const auto& node : out_nodes) {
|
||||
out_path.push_back(node.position);
|
||||
}
|
||||
for (const auto &node : out_nodes) { out_path.push_back(node.position); }
|
||||
out_path.push_back(start);
|
||||
}
|
||||
|
||||
|
@ -335,7 +334,11 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
|||
if (i - index_of_last_stored_point < 2) continue;
|
||||
bool passable = true;
|
||||
auto store_obstacle = [&](coord_t x, coord_t y) {
|
||||
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) { passable = false; };
|
||||
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) {
|
||||
passable = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
dda(tmp_path.back().x(), tmp_path.back().y(), out_path[i].x(), out_path[i].y(), store_obstacle);
|
||||
if (!passable) {
|
||||
|
|
|
@ -339,32 +339,9 @@ void MeasuringImpl::extract_features()
|
|||
}
|
||||
circles_lengths.clear(); // no longer needed, make it obvious
|
||||
|
||||
// Some of the "circles" may actually be polygons (5-8 vertices). We want them
|
||||
// detected as edges, but also to remember the center and save it into those edges.
|
||||
// We will add all such edges manually and delete the detected circles, leaving it
|
||||
// in circles_idxs so they are not picked again.
|
||||
assert(circles.size() == circles_idxs.size());
|
||||
for (int i=circles.size()-1; i>=0; --i) {
|
||||
if (circles_idxs[i].first == 0 && circles_idxs[i].second == border.size()-1) {
|
||||
int N = circles_idxs[i].second - circles_idxs[i].first;
|
||||
if (N <= 8) {
|
||||
if (N >= 5) { // polygon = 5,6,7,8 vertices
|
||||
const Vec3d center = std::get<0>(circles[i].get_circle());
|
||||
for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j)
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge,
|
||||
border[j - 1], border[j], std::make_optional(center)));
|
||||
} else {
|
||||
// This will be handled just like a regular edge (squares, triangles).
|
||||
circles_idxs.erase(circles_idxs.begin() + i);
|
||||
}
|
||||
circles.erase(circles.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Anything under 5 vertices shall not be considered a circle.
|
||||
assert(circles_idxs.size() == circles.size());
|
||||
for (int i=0; i<int(circles_idxs.size()); ++i) {
|
||||
for (int i=int(circles_idxs.size())-1; i>=0; --i) {
|
||||
const auto& [start, end] = circles_idxs[i];
|
||||
int N = start >= 0
|
||||
? end - start + (start == 0 && end == border.size()-1 ? 0 : 1) // last point is the same as first
|
||||
|
@ -372,7 +349,15 @@ void MeasuringImpl::extract_features()
|
|||
if (N < 5) {
|
||||
circles.erase(circles.begin() + i);
|
||||
circles_idxs.erase(circles_idxs.begin() + i);
|
||||
--i;
|
||||
} else if (N <= 8 && start == 0 && end == border.size()-1) {
|
||||
// This is a regular 5-8 polygon. Add the edges as edges with a special
|
||||
// point and remove the circle. Leave the indices in circles_idxs, so
|
||||
// the edges are not picked up again later.
|
||||
const Vec3d center = std::get<0>(circles[i].get_circle());
|
||||
for (int j=1; j<=end; ++j)
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge,
|
||||
border[j - 1], border[j], std::make_optional(center)));
|
||||
circles.erase(circles.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -768,54 +768,47 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
|
|||
}
|
||||
|
||||
#define EXTRA_PERIMETER_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
|
||||
//#define EXTRA_PERIM_DEBUG_FILES
|
||||
// #define EXTRA_PERIM_DEBUG_FILES
|
||||
// Function will generate extra perimeters clipped over nonbridgeable areas of the provided surface and returns both the new perimeters and
|
||||
// Polygons filled by those clipped perimeters
|
||||
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(const Surface &surface,
|
||||
ExPolygons infill_area,
|
||||
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(ExPolygons infill_area,
|
||||
const Polygons &lower_slices_polygons,
|
||||
const Flow &overhang_flow,
|
||||
size_t standard_perimeters_count,
|
||||
double scaled_resolution,
|
||||
const PrintObjectConfig &object_config,
|
||||
const PrintConfig &print_config)
|
||||
{
|
||||
coord_t anchors_size = scale_(EXTERNAL_INFILL_MARGIN);
|
||||
|
||||
ExPolygons local_area = intersection_ex({surface.expolygon}, infill_area);
|
||||
ExPolygons anchors = intersection_ex(local_area, lower_slices_polygons);
|
||||
ExPolygons overhangs = diff_ex(local_area, lower_slices_polygons);
|
||||
if (overhangs.empty()) {
|
||||
return {};
|
||||
}
|
||||
Polygons anchors = intersection(infill_area, lower_slices_polygons);
|
||||
Polygons overhangs = diff(infill_area, lower_slices_polygons);
|
||||
if (overhangs.empty()) { return {}; }
|
||||
|
||||
ExPolygons inset_anchors; // anchored area inset by the anchor length
|
||||
Polygons inset_anchors; // anchored area inset by the anchor length
|
||||
{
|
||||
std::vector<double> deltas{anchors_size * 0.15 + 0.5 * overhang_flow.scaled_spacing(),
|
||||
anchors_size * 0.33 + 0.5 * overhang_flow.scaled_spacing(),
|
||||
anchors_size * 0.66 + 0.5 * overhang_flow.scaled_spacing(), anchors_size * 1.00};
|
||||
|
||||
std::vector<ExPolygons> anchor_areas_w_delta_anchor_size{};
|
||||
std::vector<Polygons> anchor_areas_w_delta_anchor_size{};
|
||||
for (double delta : deltas) {
|
||||
anchor_areas_w_delta_anchor_size.push_back(diff_ex(anchors, offset_ex(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS)));
|
||||
anchor_areas_w_delta_anchor_size.push_back(diff(anchors, expand(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS)));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size() - 1; i++) {
|
||||
ExPolygons clipped = diff_ex(anchor_areas_w_delta_anchor_size[i], offset_ex(anchor_areas_w_delta_anchor_size[i + 1],
|
||||
Polygons clipped = diff(anchor_areas_w_delta_anchor_size[i], expand(anchor_areas_w_delta_anchor_size[i + 1],
|
||||
deltas[i + 1], EXTRA_PERIMETER_OFFSET_PARAMETERS));
|
||||
anchor_areas_w_delta_anchor_size[i] = intersection_ex(anchor_areas_w_delta_anchor_size[i],
|
||||
offset_ex(clipped, deltas[i] + deltas[i + 1],
|
||||
anchor_areas_w_delta_anchor_size[i] = intersection(anchor_areas_w_delta_anchor_size[i],
|
||||
expand(clipped, deltas[i+1] + 0.1*overhang_flow.scaled_spacing(),
|
||||
EXTRA_PERIMETER_OFFSET_PARAMETERS));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size(); i++) {
|
||||
inset_anchors.insert(inset_anchors.end(), anchor_areas_w_delta_anchor_size[i].begin(),
|
||||
anchor_areas_w_delta_anchor_size[i].end());
|
||||
inset_anchors = union_(inset_anchors, anchor_areas_w_delta_anchor_size[i]);
|
||||
}
|
||||
|
||||
inset_anchors = union_ex(inset_anchors);
|
||||
inset_anchors = closing_ex(to_polygons(inset_anchors), 1.0 * overhang_flow.scaled_spacing(), 1.0 * overhang_flow.scaled_spacing(),
|
||||
EXTRA_PERIMETER_OFFSET_PARAMETERS);
|
||||
inset_anchors = opening(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
|
||||
inset_anchors = closing(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
{
|
||||
|
@ -832,70 +825,67 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||
#endif
|
||||
}
|
||||
|
||||
ExPolygons inset_overhang_area = diff_ex(local_area, inset_anchors);
|
||||
ExPolygons repeated_area = offset_ex(intersection_ex(offset_ex(inset_overhang_area, 1.10 * overhang_flow.scaled_spacing(),
|
||||
EXTRA_PERIMETER_OFFSET_PARAMETERS),
|
||||
inset_anchors),
|
||||
0.1 * overhang_flow.scaled_spacing(), EXTRA_PERIMETER_OFFSET_PARAMETERS);
|
||||
Polygons inset_overhang_area = diff(infill_area, inset_anchors);
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
{
|
||||
BoundingBox bbox = get_extents(inset_overhangs_w_anchors);
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(debug_out_path("inset_overhangs_w_anchors").c_str(), bbox);
|
||||
for (const Line &line : to_lines(inset_overhangs_w_anchors)) svg.draw(line, "purple", scale_(0.25));
|
||||
for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "red", scale_(0.20));
|
||||
for (const Line &line : to_lines(repeated_area)) svg.draw(line, "green", scale_(0.15));
|
||||
for (const Line &line : to_lines(inset_anchors)) svg.draw(line, "yellow", scale_(0.10));
|
||||
::Slic3r::SVG svg(debug_out_path("inset_overhang_area").c_str(), bbox);
|
||||
for (const Line &line : to_lines(inset_anchors)) svg.draw(line, "purple", scale_(0.25));
|
||||
for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "red", scale_(0.15));
|
||||
svg.Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
Polygons area_left_unfilled;
|
||||
Polygons inset_overhang_area_left_unfilled;
|
||||
|
||||
std::vector<std::vector<ExtrusionPaths>> extra_perims; // overhang region -> shell -> shell parts
|
||||
for (const ExPolygon &overhang : inset_overhang_area) {
|
||||
ExPolygons overhang_to_cover = {overhang};
|
||||
overhang_to_cover.insert(overhang_to_cover.end(), repeated_area.begin(), repeated_area.end());
|
||||
overhang_to_cover = union_ex(overhang_to_cover);
|
||||
for (const ExPolygon &overhang : union_ex(to_expolygons(inset_overhang_area))) {
|
||||
Polygons overhang_to_cover = to_polygons(overhang);
|
||||
Polygons expanded_overhang_to_cover = expand(overhang_to_cover, 1.1 * overhang_flow.scaled_spacing());
|
||||
Polygons shrinked_overhang_to_cover = shrink(overhang_to_cover, 0.1 * overhang_flow.scaled_spacing());
|
||||
|
||||
ExPolygons real_overhang = intersection_ex({overhang_to_cover}, overhangs);
|
||||
if (real_overhang.empty()) {
|
||||
area_left_unfilled = union_(area_left_unfilled, to_polygons(overhang_to_cover));
|
||||
Polygons real_overhang = intersection(overhang_to_cover, overhangs);
|
||||
if (real_overhang.empty()) {
|
||||
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(), overhang_to_cover.begin(),
|
||||
overhang_to_cover.end());
|
||||
continue;
|
||||
}
|
||||
|
||||
extra_perims.emplace_back();
|
||||
std::vector<ExtrusionPaths> &overhang_region = extra_perims.back();
|
||||
|
||||
ExPolygons anchoring = intersection_ex({overhang_to_cover}, inset_anchors);
|
||||
ExPolygons perimeter_polygon = offset_ex(overhang_to_cover, -overhang_flow.scaled_spacing() * 0.6);
|
||||
Polygons anchoring = intersection(expanded_overhang_to_cover, inset_anchors);
|
||||
Polygons perimeter_polygon = offset(union_(expand(overhang_to_cover, 0.1 * overhang_flow.scaled_spacing()), anchoring),
|
||||
-overhang_flow.scaled_spacing() * 0.6);
|
||||
|
||||
double perimeter_polygon_area = area(perimeter_polygon);
|
||||
Polygon anchoring_convex_hull = Geometry::convex_hull(anchoring);
|
||||
double unbridgeable_area = area(diff_ex(perimeter_polygon, {anchoring_convex_hull}));
|
||||
for (const ExPolygon &exp : perimeter_polygon) {
|
||||
for (const Polygon &hole : exp.holes) { unbridgeable_area += std::abs(area(hole)); }
|
||||
double unbridgeable_area = area(diff(real_overhang, {anchoring_convex_hull}));
|
||||
// penalize also holes
|
||||
for (const Polygon &poly : perimeter_polygon) {
|
||||
if (poly.is_clockwise()) { // hole, penalize bridges.
|
||||
unbridgeable_area += std::abs(area(poly));
|
||||
}
|
||||
}
|
||||
auto [dir, unsupp_dist] = detect_bridging_direction(to_polygons(real_overhang), to_polygons(anchors));
|
||||
|
||||
auto [dir, unsupp_dist] = detect_bridging_direction(real_overhang, anchors);
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
{
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(debug_out_path(("bridge_check").c_str()).c_str(), bbox);
|
||||
for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "purple", scale_(0.25));
|
||||
for (const Line &line : to_lines(real_overhang)) svg.draw(line, "red", scale_(0.20));
|
||||
for (const Line &line : to_lines(anchoring_convex_hull)) svg.draw(line, "green", scale_(0.15));
|
||||
for (const Line &line : to_lines(anchoring)) svg.draw(line, "yellow", scale_(0.10));
|
||||
for (const Line &line : to_lines(diff_ex(perimeter_polygon, {anchoring_convex_hull}))) svg.draw(line, "black", scale_(0.10));
|
||||
svg.Close();
|
||||
}
|
||||
{
|
||||
BoundingBox bbox = get_extents(anchoring_convex_hull);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(debug_out_path("bridge_check").c_str(), bbox);
|
||||
for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "purple", scale_(0.25));
|
||||
for (const Line &line : to_lines(real_overhang)) svg.draw(line, "red", scale_(0.20));
|
||||
for (const Line &line : to_lines(anchoring_convex_hull)) svg.draw(line, "green", scale_(0.15));
|
||||
for (const Line &line : to_lines(anchoring)) svg.draw(line, "yellow", scale_(0.10));
|
||||
for (const Line &line : to_lines(diff_ex(perimeter_polygon, {anchoring_convex_hull}))) svg.draw(line, "black", scale_(0.10));
|
||||
svg.Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
auto total_length_of_real_overhang = total_length(to_polygons(real_overhang));
|
||||
if (unbridgeable_area < 0.2 * perimeter_polygon_area && unsupp_dist < total_length_of_real_overhang * 0.5) {
|
||||
area_left_unfilled = union_(area_left_unfilled, to_polygons(overhang_to_cover));
|
||||
if (unbridgeable_area < 0.2 * area(real_overhang) && unsupp_dist < total_length(real_overhang) * 0.125) {
|
||||
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end());
|
||||
perimeter_polygon.clear();
|
||||
} else {
|
||||
// fill the overhang with perimeters
|
||||
|
@ -903,16 +893,15 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||
while (continuation_loops > 0) {
|
||||
auto prev = perimeter_polygon;
|
||||
// prepare next perimeter lines
|
||||
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), inset_overhang_area);
|
||||
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
|
||||
|
||||
// do not add the perimeter to result yet, first check if perimeter_polygon is not empty after shrinking - this would mean
|
||||
// that the polygon was possibly too small for full perimeter loop and in that case try gap fill first
|
||||
perimeter_polygon.insert(perimeter_polygon.end(), anchoring.begin(), anchoring.end());
|
||||
perimeter_polygon = union_ex(perimeter_polygon);
|
||||
perimeter_polygon = intersection_ex({overhang_to_cover}, offset_ex(perimeter_polygon, -overhang_flow.scaled_spacing()));
|
||||
perimeter_polygon = union_(perimeter_polygon, anchoring);
|
||||
perimeter_polygon = intersection(offset(perimeter_polygon, -overhang_flow.scaled_spacing()), expanded_overhang_to_cover);
|
||||
|
||||
if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width
|
||||
ExPolygons shrinked = offset_ex(prev, -0.4 * overhang_flow.scaled_spacing());
|
||||
Polygons shrinked = offset(prev, -0.4 * overhang_flow.scaled_spacing());
|
||||
if (!shrinked.empty()) {
|
||||
overhang_region.emplace_back();
|
||||
extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||
|
@ -922,8 +911,10 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||
Polylines fills;
|
||||
ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) :
|
||||
offset_ex(prev, -overhang_flow.scaled_spacing() * 0.5);
|
||||
|
||||
//gap = expolygons_simplify(gap, overhang_flow.scaled_spacing());
|
||||
for (const ExPolygon &ep : gap) {
|
||||
ep.medial_axis(overhang_flow.scaled_spacing() * 2.0, 0.35 * overhang_flow.scaled_width(), &fills);
|
||||
ep.medial_axis(overhang_flow.scaled_spacing() * 2.0, 0.3 * overhang_flow.scaled_width(), &fills);
|
||||
}
|
||||
if (!fills.empty()) {
|
||||
fills = intersection_pl(fills, inset_overhang_area);
|
||||
|
@ -938,10 +929,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||
overhang_flow.width(), overhang_flow.height());
|
||||
}
|
||||
|
||||
if (intersection(perimeter_polygon, real_overhang).empty()) {
|
||||
continuation_loops--;
|
||||
}
|
||||
|
||||
if (intersection(perimeter_polygon, real_overhang).empty()) { continuation_loops--; }
|
||||
|
||||
if (prev == perimeter_polygon) {
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
|
@ -957,15 +945,14 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||
break;
|
||||
}
|
||||
}
|
||||
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), inset_overhang_area);
|
||||
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
|
||||
overhang_region.emplace_back();
|
||||
extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||
overhang_flow.width(), overhang_flow.height());
|
||||
|
||||
perimeter_polygon = offset_ex(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
|
||||
perimeter_polygon.insert(perimeter_polygon.end(), anchoring.begin(), anchoring.end());
|
||||
perimeter_polygon = union_ex(perimeter_polygon);
|
||||
area_left_unfilled = union_(area_left_unfilled, to_polygons(perimeter_polygon));
|
||||
perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
|
||||
perimeter_polygon = union_(perimeter_polygon, anchoring);
|
||||
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(), perimeter_polygon.begin(),perimeter_polygon.end());
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
|
@ -974,30 +961,32 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||
for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "blue", scale_(0.05));
|
||||
for (const Line &line : to_lines(anchoring)) svg.draw(line, "green", scale_(0.05));
|
||||
for (const Line &line : to_lines(overhang_to_cover)) svg.draw(line, "yellow", scale_(0.05));
|
||||
for (const Line &line : to_lines(area_left_unfilled)) svg.draw(line, "red", scale_(0.05));
|
||||
for (const Line &line : to_lines(inset_overhang_area_left_unfilled)) svg.draw(line, "red", scale_(0.05));
|
||||
svg.Close();
|
||||
#endif
|
||||
|
||||
std::reverse(overhang_region.begin(), overhang_region.end()); //reverse the order, It shall be printed from inside out
|
||||
std::reverse(overhang_region.begin(), overhang_region.end()); // reverse the order, It shall be printed from inside out
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ExtrusionPaths> result{};
|
||||
for (const std::vector<ExtrusionPaths> &paths : extra_perims) {
|
||||
result.push_back(sort_and_connect_extra_perimeters(paths, 1.5 * overhang_flow.scaled_spacing()));
|
||||
result.push_back(sort_and_connect_extra_perimeters(paths, 2.0 * overhang_flow.scaled_spacing()));
|
||||
}
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
bbox.offset(scale_(2.));
|
||||
::Slic3r::SVG svg(debug_out_path(("final" + std::to_string(rand())).c_str()).c_str(), bbox);
|
||||
for (const Line &line : to_lines(area_left_unfilled)) svg.draw(line, "blue", scale_(0.05));
|
||||
for (const Line &line : to_lines(inset_overhangs_w_anchors)) svg.draw(line, "green", scale_(0.05));
|
||||
for (const Line &line : to_lines(diff(inset_overhangs_w_anchors, area_left_unfilled))) svg.draw(line, "yellow", scale_(0.05));
|
||||
for (const Line &line : to_lines(inset_overhang_area_left_unfilled)) svg.draw(line, "blue", scale_(0.05));
|
||||
for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "green", scale_(0.05));
|
||||
for (const Line &line : to_lines(diff(inset_overhang_area, inset_overhang_area_left_unfilled))) svg.draw(line, "yellow", scale_(0.05));
|
||||
svg.Close();
|
||||
#endif
|
||||
|
||||
return {result, diff(inset_overhang_area, area_left_unfilled)};
|
||||
inset_overhang_area_left_unfilled = union_(inset_overhang_area_left_unfilled);
|
||||
|
||||
return {result, diff(inset_overhang_area, inset_overhang_area_left_unfilled)};
|
||||
}
|
||||
|
||||
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
|
||||
|
@ -1215,34 +1204,26 @@ void PerimeterGenerator::process_arachne()
|
|||
// collapse too narrow infill areas
|
||||
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||
// append infill areas to fill_surfaces
|
||||
this->fill_surfaces->append(
|
||||
offset2_ex(
|
||||
union_ex(pp),
|
||||
float(- min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.)),
|
||||
stInternal);
|
||||
// append infill areas to fill_surfaces
|
||||
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.));
|
||||
this->fill_surfaces->append(infill_areas, stInternal);
|
||||
|
||||
if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs &&
|
||||
this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) {
|
||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||
ExPolygons infill_area;
|
||||
for (const auto &internal_surface : this->fill_surfaces->surfaces) { infill_area.push_back(internal_surface.expolygon); }
|
||||
auto [extra_perimeters,
|
||||
filled_area] = generate_extra_perimeters_over_overhangs(surface, infill_area,
|
||||
this->lower_slices_polygons(),
|
||||
this->overhang_flow, this->config->perimeters,
|
||||
this->m_scaled_resolution,
|
||||
*this->object_config, *this->print_config);
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
||||
this->lower_slices_polygons(),
|
||||
this->overhang_flow, this->m_scaled_resolution,
|
||||
*this->object_config, *this->print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
|
||||
ExtrusionEntityCollection new_perimeters{};
|
||||
ExtrusionEntityCollection new_perimeters{};
|
||||
new_perimeters.no_sort = this_islands_perimeters->no_sort;
|
||||
for (const ExtrusionPaths& paths : extra_perimeters) {
|
||||
new_perimeters.append(paths);
|
||||
}
|
||||
for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
|
||||
new_perimeters.append(this_islands_perimeters->entities);
|
||||
this_islands_perimeters->swap(new_perimeters);
|
||||
|
||||
|
||||
SurfaceCollection orig_surfaces = *this->fill_surfaces;
|
||||
this->fill_surfaces->clear();
|
||||
for (const auto &surface : orig_surfaces.surfaces) {
|
||||
|
@ -1516,33 +1497,24 @@ void PerimeterGenerator::process_classic()
|
|||
// collapse too narrow infill areas
|
||||
coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||
// append infill areas to fill_surfaces
|
||||
this->fill_surfaces->append(
|
||||
offset2_ex(
|
||||
union_ex(pp),
|
||||
float(- inset - min_perimeter_infill_spacing / 2.),
|
||||
float(min_perimeter_infill_spacing / 2.)),
|
||||
stInternal);
|
||||
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-inset - min_perimeter_infill_spacing / 2.),
|
||||
float(min_perimeter_infill_spacing / 2.));
|
||||
this->fill_surfaces->append(infill_areas, stInternal);
|
||||
|
||||
if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs &&
|
||||
this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) {
|
||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||
ExPolygons infill_area;
|
||||
for (const auto &internal_surface : this->fill_surfaces->surfaces) { infill_area.push_back(internal_surface.expolygon); }
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(surface, infill_area,
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
||||
this->lower_slices_polygons(),
|
||||
this->overhang_flow, this->config->perimeters,
|
||||
this->m_scaled_resolution,
|
||||
this->overhang_flow, this->m_scaled_resolution,
|
||||
*this->object_config, *this->print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
|
||||
ExtrusionEntityCollection new_perimeters{};
|
||||
ExtrusionEntityCollection new_perimeters{};
|
||||
new_perimeters.no_sort = this_islands_perimeters->no_sort;
|
||||
for (const ExtrusionPaths& paths : extra_perimeters) {
|
||||
new_perimeters.append(paths);
|
||||
}
|
||||
for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
|
||||
new_perimeters.append(this_islands_perimeters->entities);
|
||||
this_islands_perimeters->swap(new_perimeters);
|
||||
|
||||
|
||||
SurfaceCollection orig_surfaces = *this->fill_surfaces;
|
||||
this->fill_surfaces->clear();
|
||||
|
|
|
@ -58,7 +58,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
// Cache the plenty of parameters, which influence the G-code generator only,
|
||||
// or they are only notes not influencing the generated G-code.
|
||||
static std::unordered_set<std::string> steps_gcode = {
|
||||
"avoid_curled_filament_during_travels",
|
||||
"avoid_crossing_perimeters",
|
||||
"avoid_crossing_perimeters_max_detour",
|
||||
"bed_shape",
|
||||
|
@ -223,6 +222,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
osteps.emplace_back(posInfill);
|
||||
osteps.emplace_back(posSupportMaterial);
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (opt_key == "avoid_curled_filament_during_travels") {
|
||||
osteps.emplace_back(posEstimateCurledExtrusions);
|
||||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
//FIXME invalidate all steps of all objects as well?
|
||||
|
|
|
@ -1231,13 +1231,13 @@ struct LayerCurlingEstimator
|
|||
|
||||
if (fabs(dist_from_prev_layer) < 2.0f * flow_width) {
|
||||
const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx);
|
||||
current_line.malformation += 0.85 * nearest_line.malformation;
|
||||
current_line.malformation += 0.9 * nearest_line.malformation;
|
||||
}
|
||||
if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) {
|
||||
float factor = std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) /
|
||||
(max_malformation_dist - min_malformation_dist);
|
||||
float factor = 0.5f + 0.5f * std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) /
|
||||
(max_malformation_dist - min_malformation_dist);
|
||||
malformation_acc.add_distance(current_line.len);
|
||||
current_line.malformation += l->height * factor * (2.0f + 3.0f * (malformation_acc.max_curvature / PI));
|
||||
current_line.malformation += l->height * factor * (1.5f + 3.0f * (malformation_acc.max_curvature / PI));
|
||||
current_line.malformation = std::min(current_line.malformation, float(l->height * params.max_malformation_factor));
|
||||
} else {
|
||||
malformation_acc.reset();
|
||||
|
|
|
@ -26,7 +26,7 @@ struct Params {
|
|||
// the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2]
|
||||
const float bridge_distance = 12.0f; //mm
|
||||
const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (1 + this factor * (curvature / PI) )
|
||||
const std::pair<float,float> malformation_overlap_factor = std::pair<float, float> { 0.45, -0.1 };
|
||||
const std::pair<float,float> malformation_overlap_factor = std::pair<float, float> { 0.50, -0.1 };
|
||||
const float max_malformation_factor = 10.0f;
|
||||
|
||||
const float min_distance_between_support_points = 3.0f; //mm
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue