Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Smooth" outer perimeters #8443

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/libslic3r/ExtrusionEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const

// now split path_idx in two parts
const ExtrusionPath &path = this->paths[path_idx];
ExtrusionPath p1(path.overhang_degree, path.curve_degree, path.role(), path.mm3_per_mm, path.width, path.height);
ExtrusionPath p2(path.overhang_degree, path.curve_degree, path.role(), path.mm3_per_mm, path.width, path.height);
ExtrusionPath p1(path.overhang_degree, path.curve_degree, path.role(), path.mm3_per_mm, path.width, path.height, path.z_offset);
ExtrusionPath p2(path.overhang_degree, path.curve_degree, path.role(), path.mm3_per_mm, path.width, path.height, path.z_offset);
path.polyline.split_at(p, &p1.polyline, &p2.polyline);

if (this->paths.size() == 1) {
Expand Down
25 changes: 17 additions & 8 deletions src/libslic3r/ExtrusionEntity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,13 @@ class ExtrusionPath : public ExtrusionEntity
// Height of the extrusion, used for visualization purposes.
float height;

ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1), m_role(erNone), m_no_extrusion(false) {}
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role), m_no_extrusion(false) {}
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height, bool no_extrusion = false) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role), m_no_extrusion(no_extrusion) {}
ExtrusionPath(double overhang_degree, int curve_degree, ExtrusionRole role, double mm3_per_mm, float width, float height) : overhang_degree(overhang_degree), curve_degree(curve_degree), mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
// Z offset relative to current layer, will be added to current layer height to offset z height.
float z_offset;

ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1), m_role(erNone), m_no_extrusion(false), z_offset(0.0) {}
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role), m_no_extrusion(false), z_offset(0.0) {}
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height, bool no_extrusion = false) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role), m_no_extrusion(no_extrusion), z_offset(0.0) {}
ExtrusionPath(double overhang_degree, int curve_degree, ExtrusionRole role, double mm3_per_mm, float width, float height, float z_offset = 0.f) : overhang_degree(overhang_degree), curve_degree(curve_degree), mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role), z_offset(z_offset) {}

ExtrusionPath(const ExtrusionPath &rhs)
: polyline(rhs.polyline)
Expand All @@ -175,6 +178,7 @@ class ExtrusionPath : public ExtrusionEntity
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
, z_offset(rhs.z_offset)
{}
ExtrusionPath(ExtrusionPath &&rhs)
: polyline(std::move(rhs.polyline))
Expand All @@ -186,6 +190,7 @@ class ExtrusionPath : public ExtrusionEntity
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
, z_offset(rhs.z_offset)
{}
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs)
: polyline(polyline)
Expand All @@ -197,6 +202,7 @@ class ExtrusionPath : public ExtrusionEntity
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
, z_offset(rhs.z_offset)
{}
ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs)
: polyline(std::move(polyline))
Expand All @@ -208,6 +214,7 @@ class ExtrusionPath : public ExtrusionEntity
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
, z_offset(rhs.z_offset)
{}

ExtrusionPath& operator=(const ExtrusionPath& rhs) {
Expand All @@ -220,6 +227,7 @@ class ExtrusionPath : public ExtrusionEntity
this->overhang_degree = rhs.overhang_degree;
this->curve_degree = rhs.curve_degree;
this->polyline = rhs.polyline;
this->z_offset = rhs.z_offset;
return *this;
}
ExtrusionPath& operator=(ExtrusionPath&& rhs) {
Expand All @@ -232,6 +240,7 @@ class ExtrusionPath : public ExtrusionEntity
this->overhang_degree = rhs.overhang_degree;
this->curve_degree = rhs.curve_degree;
this->polyline = std::move(rhs.polyline);
this->z_offset = rhs.z_offset;
return *this;
}

Expand Down Expand Up @@ -551,22 +560,22 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, E
polylines.clear();
}

inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, double overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height)
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, double overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height, float z_offset = 0.f)
{
dst.reserve(dst.size() + polylines.size());
for (Polyline &polyline : polylines)
if (polyline.is_valid()) {
dst.push_back(ExtrusionPath(overhang_degree, curva_degree, role, mm3_per_mm, width, height));
dst.push_back(ExtrusionPath(overhang_degree, curva_degree, role, mm3_per_mm, width, height, z_offset));
dst.back().polyline = std::move(polyline);
}
polylines.clear();
}

inline void extrusion_paths_append(ExtrusionPaths &dst, Polyline &&polyline, double overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height)
inline void extrusion_paths_append(ExtrusionPaths &dst, Polyline &&polyline, double overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height, float z_offset = 0.f)
{
dst.reserve(dst.size() + 1);
if (polyline.is_valid()) {
dst.push_back(ExtrusionPath(overhang_degree, curva_degree, role, mm3_per_mm, width, height));
dst.push_back(ExtrusionPath(overhang_degree, curva_degree, role, mm3_per_mm, width, height, z_offset));
dst.back().polyline = std::move(polyline);
}
polyline.clear();
Expand Down
22 changes: 17 additions & 5 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3979,7 +3979,9 @@ LayerResult GCode::process_layer(
// The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
std::vector<unsigned int> printing_extruders;
for (const ObjectByExtruder::Island::Region::Type entity_type : { ObjectByExtruder::Island::Region::INFILL, ObjectByExtruder::Island::Region::PERIMETERS }) {
for (const ExtrusionEntity *ee : (entity_type == ObjectByExtruder::Island::Region::INFILL) ? layerm->fills.entities : layerm->perimeters.entities) {
ExtrusionEntitiesPtr entities = entity_type == ObjectByExtruder::Island::Region::INFILL ? layerm->fills.entities : layerm->perimeters.entities;
for (size_t entity_idx = 0; entity_idx < entities.size(); entity_idx++) {
const ExtrusionEntity *ee = entities[entity_idx];
// extrusions represents infill or perimeter extrusions of a single island.
assert(dynamic_cast<const ExtrusionEntityCollection*>(ee) != nullptr);
const auto *extrusions = static_cast<const ExtrusionEntityCollection*>(ee);
Expand Down Expand Up @@ -4013,7 +4015,16 @@ LayerResult GCode::process_layer(
}
} else
printing_extruders.emplace_back(correct_extruder_id);


// For the perimeters of the upper sub slices we skip the overlapping test and put all extrusions into the last island
// This is to keep the extrusion order from the perimeter generator.
bool skip_test = false;
if (entity_type == ObjectByExtruder::Island::Region::PERIMETERS) {
const int perimeter_divider = config().outer_perimeter_layer_divider;
if (perimeter_divider > 1 && entity_idx >= (entities.size() - perimeter_divider + 1)) {
skip_test = true;
}
}
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
for (unsigned int extruder : printing_extruders)
{
Expand All @@ -4023,7 +4034,7 @@ LayerResult GCode::process_layer(
&layer_to_print - layers.data(),
layers.size(), n_slices+1);
for (size_t i = 0; i <= n_slices; ++ i) {
bool last = i == n_slices;
bool last = i == n_slices || skip_test;
size_t island_idx = last ? n_slices : slices_test_order[i];
if (// extrusions->first_point does not fit inside any slice
last ||
Expand Down Expand Up @@ -5102,7 +5113,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,

const auto get_sloped_z = [&sloped, this](double z_ratio) {
const auto height = sloped->height;
return lerp(m_nominal_z - height, m_nominal_z, z_ratio);
const auto z_offset = sloped->z_offset;
return lerp(m_nominal_z + z_offset - height, m_nominal_z + z_offset, z_ratio);
};

// go to first point of extrusion path
Expand All @@ -5113,7 +5125,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
path.first_point(),
path.role(),
"move to first " + description + " point",
sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio)
sloped == nullptr ? m_nominal_z + path.z_offset : get_sloped_z(sloped->slope_begin.z_ratio)
);
m_need_change_layer_lift_z = false;
}
Expand Down
159 changes: 118 additions & 41 deletions src/libslic3r/Layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,32 +37,94 @@ LayerRegion* Layer::add_region(const PrintRegion *print_region)
// merge all regions' slices to get islands
void Layer::make_slices()
{
ExPolygons slices;
if (m_regions.size() == 1) {
// optimization: if we only have one region, take its slices
slices = to_expolygons(m_regions.front()->slices.surfaces);
} else {
Polygons slices_p;
for (LayerRegion *layerm : m_regions)
polygons_append(slices_p, to_polygons(layerm->slices.surfaces));
slices = union_safety_offset_ex(slices_p);
{
ExPolygons slices;
if (m_regions.size() == 1) {
// optimization: if we only have one region, take its slices
slices = to_expolygons(m_regions.front()->slices.surfaces);
} else {
Polygons slices_p;
for (LayerRegion *layerm : m_regions)
polygons_append(slices_p, to_polygons(layerm->slices.surfaces));
slices = union_safety_offset_ex(slices_p);
}

this->lslices.clear();
this->lslices.reserve(slices.size());

// prepare ordering points
Points ordering_points;
ordering_points.reserve(slices.size());
for (const ExPolygon &ex : slices)
ordering_points.push_back(ex.contour.first_point());

// sort slices
std::vector<Points::size_type> order = chain_points(ordering_points);

// populate slices vector
for (size_t i : order)
this->lslices.emplace_back(std::move(slices[i]));
}

this->lslices.clear();
this->lslices.reserve(slices.size());

// prepare ordering points
Points ordering_points;
ordering_points.reserve(slices.size());
for (const ExPolygon &ex : slices)
ordering_points.push_back(ex.contour.first_point());

// sort slices
std::vector<Points::size_type> order = chain_points(ordering_points);
// sub slices
{
std::vector<std::pair<float, ExPolygons>> sub_slices;
ExPolygons sub_slices_merged;
if (m_regions.size() == 1) {
// optimization: if we only have one region, take its slices
for (auto& [z, s] : m_regions.front()->sub_slices) {
sub_slices.emplace_back(z, s);
sub_slices_merged = union_ex(sub_slices_merged, s);
}
} else {
std::map<float, Polygons> slices_p;
for (LayerRegion* layerm : m_regions)
for (auto& [z, s] : layerm->sub_slices) {
auto it = slices_p.find(z);
if (it != slices_p.end()) {
polygons_append(it->second, to_polygons(s));
} else {
slices_p.emplace(z, to_polygons(s));
}
}
for (auto& [z, p] : slices_p) {
ExPolygons tmp = union_safety_offset_ex(p);
sub_slices.emplace_back(z, tmp);
sub_slices_merged = union_ex(sub_slices_merged, tmp);

}
}
this->lsub_slices.clear();
for (auto& [z, s] : sub_slices) {
// prepare ordering points
Points ordering_points;
ordering_points.reserve(s.size());
for (const ExPolygon& ex : s)
ordering_points.push_back(ex.contour.first_point());

// sort slices
std::vector<Points::size_type> order = chain_points(ordering_points);

// populate slices vector
ExPolygons final_slices;
for (size_t i : order)
final_slices.emplace_back(std::move(s[i]));
this->lsub_slices.emplace_back(z, final_slices);
}

// populate slices vector
for (size_t i : order)
this->lslices.emplace_back(std::move(slices[i]));
this->lsub_slices_merged.clear();
Points ordering_points;
ordering_points.reserve(sub_slices_merged.size());
for (const ExPolygon &ex : sub_slices_merged)
ordering_points.push_back(ex.contour.first_point());

// sort slices
std::vector<Points::size_type> order = chain_points(ordering_points);

// populate slices vector
for (size_t i : order)
this->lsub_slices_merged.emplace_back(std::move(sub_slices_merged[i]));
}
}

static inline bool layer_needs_raw_backup(const Layer *layer)
Expand Down Expand Up @@ -206,31 +268,46 @@ void Layer::make_perimeters()

if (layerms.size() == 1) { // optimization
(*layerm)->fill_surfaces.surfaces.clear();
(*layerm)->make_perimeters((*layerm)->slices, {*layerm}, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons);
(*layerm)->make_perimeters((*layerm)->slices, (*layerm)->sub_slices, {*layerm}, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons);
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
} else {
SurfaceCollection new_slices;
// Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the infill existence.
LayerRegion *layerm_config = layerms.front();
{
// group slices (surfaces) according to number of extra perimeters
std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegion *layerm : layerms) {
for (const Surface &surface : layerm->slices.surfaces)
slices[surface.extra_perimeters].emplace_back(surface);
if (layerm->region().config().sparse_infill_density > layerm_config->region().config().sparse_infill_density)
layerm_config = layerm;
}
// merge the surfaces assigned to each group
for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices)
new_slices.append(offset_ex(surfaces_with_extra_perimeters.second, ClipperSafetyOffset), surfaces_with_extra_perimeters.second.front());
}

std::map<float, ExPolygons> new_sub_slices_map;
// Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the
// infill existence.
LayerRegion* layerm_config = layerms.front();
{
// group slices (surfaces) according to number of extra perimeters
std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegion* layerm : layerms) {
for (const Surface& surface : layerm->slices.surfaces)
slices[surface.extra_perimeters].emplace_back(surface);
for (const auto& [z, slice] : layerm->sub_slices) {
auto it = new_sub_slices_map.find(z);
if (it == new_sub_slices_map.end()) {
new_sub_slices_map.emplace(z, slice);
} else {
it->second.insert(it->second.begin(), slice.begin(), slice.end());
}
}
if (layerm->region().config().sparse_infill_density > layerm_config->region().config().sparse_infill_density)
layerm_config = layerm;
}
// merge the surfaces assigned to each group
for (std::pair<const unsigned short, Surfaces>& surfaces_with_extra_perimeters : slices)
new_slices.append(offset_ex(surfaces_with_extra_perimeters.second, ClipperSafetyOffset),
surfaces_with_extra_perimeters.second.front());
}
SubSliceCollection new_sub_slices;
for (auto& [z, slice] : new_sub_slices_map) {
new_sub_slices.emplace_back(z, std::move(slice));
}

// make perimeters
SurfaceCollection fill_surfaces;
//BBS
ExPolygons fill_no_overlap;
layerm_config->make_perimeters(new_slices, layerms, &fill_surfaces, &fill_no_overlap);
layerm_config->make_perimeters(new_slices, new_sub_slices, layerms, &fill_surfaces, &fill_no_overlap);

// assign fill_surfaces to each layer
if (!fill_surfaces.surfaces.empty()) {
Expand Down
Loading
Loading