mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 07:27:41 -06:00
Seam: use scarf joint to minimize seam visiblity (#3839)
* Remember z of previous layer
* Support travel to middle of the layer z
* Support sloped extrusion
* Implement sloped seam
* Reduce extra movements
* Don't clip loop if sloped seam is enabled
* Fix wipe
* Ensure `slope_max_segment_length`
* Add options
* Limit slope length to perimeter length
* Fix slope segmentation
* Rename the option to scarf joint seam
* Don't modify the slope option when turning on spiral vase
* Add a few suggestions when turnning on scarf joint
* Add option to add scarf joint to inner walls
* Apply seam gap at the end of the slope
* Add option to explicitly use the entire loop as scarf length
* Fix layer number
* Increase default scarf length to 20mm
* Better way of storing the global scarf state
* Better vase mode layer height recognition
* Move id should exclude seams
* Fix slope height with independent support layer height
* Fix linux build
* Allow controlling the scarf with modifier
* Scarf start height default to 0
* Allow enable scarf seam on contour only
* Fix type error
* Move the creation of sloped loop into ExtrusionEntity.cpp
* Fix error "vector too long"
* Detect seams properly
* The correct way of calculating the rate limit
* The correct way of calculating the rate limit
(cherry picked from commit 05961f7c98
)
* Add pressure equalizer in print by object mode
* Remove the settings recommendation as it varies a lot depends on printer & filament
* Add a beta suffix
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
ab1b0e0ebc
commit
924a2b4551
16 changed files with 533 additions and 54 deletions
|
@ -4534,7 +4534,6 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
|
|||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters)
|
||||
{
|
||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||
|
@ -4557,11 +4556,17 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
} else
|
||||
loop.split_at(last_pos, false);
|
||||
|
||||
const auto seam_scarf_type = m_config.seam_slope_type.value;
|
||||
const bool enable_seam_slope = ((seam_scarf_type == SeamScarfType::External && !is_hole) || seam_scarf_type == SeamScarfType::All) &&
|
||||
!m_config.spiral_mode &&
|
||||
(loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) &&
|
||||
layer_id() > 0;
|
||||
|
||||
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
// we discard it in that case
|
||||
double clip_length = m_enable_loop_clipping ?
|
||||
scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter))) : 0;
|
||||
const double seam_gap = scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter)));
|
||||
const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0;
|
||||
|
||||
// get paths
|
||||
ExtrusionPaths paths;
|
||||
|
@ -4650,15 +4655,54 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool is_small_peri = false;
|
||||
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||
// description += ExtrusionLoop::role_to_string(loop.loop_role());
|
||||
// description += ExtrusionEntity::role_to_string(path->role);
|
||||
|
||||
const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath& path) {
|
||||
// don't apply small perimeter setting for overhangs/bridges/non-perimeters
|
||||
is_small_peri = is_perimeter(path->role()) && !is_bridge(path->role()) && small_peri_speed > 0 && (path->get_overhang_degree() == 0 || path->get_overhang_degree() > 5);
|
||||
gcode += this->_extrude(*path, description, is_small_peri ? small_peri_speed : speed);
|
||||
const bool is_small_peri = is_perimeter(path.role()) && !is_bridge(path.role()) && small_peri_speed > 0 && (path.get_overhang_degree() == 0 || path.get_overhang_degree() > 5);
|
||||
return is_small_peri ? small_peri_speed : speed;
|
||||
};
|
||||
|
||||
if (!enable_seam_slope) {
|
||||
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||
gcode += this->_extrude(*path, description, speed_for_path(*path));
|
||||
}
|
||||
} else {
|
||||
// Create seam slope
|
||||
double start_slope_ratio;
|
||||
if (m_config.seam_slope_start_height.percent) {
|
||||
start_slope_ratio = m_config.seam_slope_start_height.value / 100.;
|
||||
} else {
|
||||
// Get the ratio against current layer height
|
||||
double h = paths.front().height;
|
||||
start_slope_ratio = m_config.seam_slope_start_height.value / h;
|
||||
}
|
||||
|
||||
double loop_length = 0.;
|
||||
for (const auto & path : paths) {
|
||||
loop_length += unscale_(path.length());
|
||||
}
|
||||
|
||||
const bool slope_entire_loop = m_config.seam_slope_entire_loop;
|
||||
const double slope_min_length = slope_entire_loop ? loop_length : std::min(m_config.seam_slope_min_length.value, loop_length);
|
||||
const int slope_steps = m_config.seam_slope_steps;
|
||||
const double slope_max_segment_length = scale_(slope_min_length / slope_steps);
|
||||
|
||||
// Calculate the sloped loop
|
||||
ExtrusionLoopSloped new_loop(paths, seam_gap, slope_min_length, slope_max_segment_length, start_slope_ratio, loop.loop_role());
|
||||
|
||||
// Then extrude it
|
||||
for (const auto& p : new_loop.get_all_paths()) {
|
||||
gcode += this->_extrude(*p, description, speed_for_path(*p));
|
||||
}
|
||||
|
||||
// Fix path for wipe
|
||||
if (!new_loop.ends.empty()) {
|
||||
paths.clear();
|
||||
// The start slope part is ignored as it overlaps with the end part
|
||||
paths.reserve(new_loop.paths.size() + new_loop.ends.size());
|
||||
paths.insert(paths.end(), new_loop.paths.begin(), new_loop.paths.end());
|
||||
paths.insert(paths.end(), new_loop.ends.begin(), new_loop.ends.end());
|
||||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
|
@ -4932,14 +4976,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
if (is_bridge(path.role()))
|
||||
description += " (bridge)";
|
||||
|
||||
const ExtrusionPathSloped* sloped = dynamic_cast<const ExtrusionPathSloped*>(&path);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
// go to first point of extrusion path
|
||||
//BBS: path.first_point is 2D point. But in lazy raise case, lift z is done in travel_to function.
|
||||
//Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance
|
||||
if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z) {
|
||||
if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z || (sloped != nullptr && !sloped->is_flat())) {
|
||||
gcode += this->travel_to(
|
||||
path.first_point(),
|
||||
path.role(),
|
||||
"move to first " + description + " point"
|
||||
"move to first " + description + " point",
|
||||
sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio)
|
||||
);
|
||||
m_need_change_layer_lift_z = false;
|
||||
}
|
||||
|
@ -5290,7 +5342,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
if (!variable_speed) {
|
||||
// F is mm per minute.
|
||||
gcode += m_writer.set_speed(F, "", comment);
|
||||
double path_length = 0.;
|
||||
{
|
||||
if (m_enable_cooling_markers) {
|
||||
if (enable_overhang_bridge_fan) {
|
||||
|
@ -5323,9 +5374,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
}
|
||||
}
|
||||
}
|
||||
// BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode
|
||||
// BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode or we are doing sloped extrusion
|
||||
// Attention: G2 and G3 is not supported in spiral_mode mode
|
||||
if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode) {
|
||||
if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr) {
|
||||
double path_length = 0.;
|
||||
double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR;
|
||||
for (const Line& line : path.polyline.lines()) {
|
||||
std::string tempDescription = description;
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
|
@ -5339,10 +5392,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
|
||||
}
|
||||
}
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
dE,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
|
||||
if (sloped == nullptr) {
|
||||
// Normal extrusion
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
dE,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
|
||||
} else {
|
||||
// Sloped extrusion
|
||||
const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
|
||||
Vec2d dest2d = this->point_to_gcode(line.b);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio));
|
||||
gcode += m_writer.extrude_to_xyz(
|
||||
dest3d,
|
||||
dE * e_ratio,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// BBS: start to generate gcode from arc fitting data which includes line and arc
|
||||
|
@ -5356,7 +5421,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) {
|
||||
const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
auto dE = e_per_mm * line_length;
|
||||
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
|
||||
auto oldE = dE;
|
||||
|
@ -5378,7 +5442,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
const ArcSegment& arc = fitting_result[fitting_index].arc_data;
|
||||
const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR;
|
||||
const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point);
|
||||
path_length += arc_length;
|
||||
auto dE = e_per_mm * arc_length;
|
||||
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
|
||||
auto oldE = dE;
|
||||
|
@ -5407,6 +5470,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
} else {
|
||||
double last_set_speed = new_points[0].speed * 60.0;
|
||||
|
||||
double total_length = 0;
|
||||
if (sloped != nullptr) {
|
||||
// Calculate total extrusion length
|
||||
Points p;
|
||||
p.reserve(new_points.size());
|
||||
std::transform(new_points.begin(), new_points.end(), std::back_inserter(p), [](const ProcessedPoint& pp) { return pp.p; });
|
||||
Polyline l(p);
|
||||
total_length = l.length() * SCALING_FACTOR;
|
||||
}
|
||||
gcode += m_writer.set_speed(last_set_speed, "", comment);
|
||||
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
|
||||
bool pre_fan_enabled = false;
|
||||
|
@ -5414,6 +5486,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
if( m_enable_cooling_markers && enable_overhang_bridge_fan)
|
||||
pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role());
|
||||
|
||||
double path_length = 0.;
|
||||
for (size_t i = 1; i < new_points.size(); i++) {
|
||||
std::string tempDescription = description;
|
||||
const ProcessedPoint &processed_point = new_points[i];
|
||||
|
@ -5449,6 +5522,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
}
|
||||
|
||||
const double line_length = (p - prev).norm();
|
||||
path_length += line_length;
|
||||
double new_speed = pre_processed_point.speed * 60.0;
|
||||
if (last_set_speed != new_speed) {
|
||||
gcode += m_writer.set_speed(new_speed, "", comment);
|
||||
|
@ -5463,8 +5537,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
|
||||
}
|
||||
}
|
||||
gcode +=
|
||||
m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
if (sloped == nullptr) {
|
||||
// Normal extrusion
|
||||
gcode += m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
} else {
|
||||
// Sloped extrusion
|
||||
const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
|
||||
Vec3d dest3d(p(0), p(1), get_sloped_z(z_ratio));
|
||||
gcode += m_writer.extrude_to_xyz(dest3d, dE * e_ratio, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
}
|
||||
|
||||
prev = p;
|
||||
|
||||
|
@ -5543,7 +5624,7 @@ std::string GCode::_encode_label_ids_to_base64(std::vector<size_t> ids)
|
|||
}
|
||||
|
||||
// This method accepts &point in print coordinates.
|
||||
std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)
|
||||
std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string comment, double z/* = DBL_MAX*/)
|
||||
{
|
||||
/* Define the travel move as a line between current position and the taget point.
|
||||
This is expressed in print coordinates, so it will need to be translated by
|
||||
|
@ -5628,15 +5709,36 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
|
||||
// use G1 because we rely on paths being straight (G0 may make round paths)
|
||||
if (travel.size() >= 2) {
|
||||
for (size_t i = 1; i < travel.size(); ++ i) {
|
||||
// BBS. Process lazy layer change, but don't do lazy layer change when enable spiral vase
|
||||
Vec3d curr_pos = m_writer.get_position();
|
||||
if (i == 1 && !m_spiral_vase) {
|
||||
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment+" travel_to_xyz");
|
||||
if (m_spiral_vase) {
|
||||
// No lazy z lift for spiral vase mode
|
||||
for (size_t i = 1; i < travel.size(); ++i) {
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy");
|
||||
}
|
||||
} else {
|
||||
if (travel.size() == 2) {
|
||||
// No extra movements emitted by avoid_crossing_perimeters, simply move to the end point with z change
|
||||
const auto& dest2d = this->point_to_gcode(travel.points.back());
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), z == DBL_MAX ? m_nominal_z : z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
|
||||
} else {
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment+" travel_to_xy");
|
||||
// Extra movements emitted by avoid_crossing_perimeters, lift the z to normal height at the beginning, then apply the z
|
||||
// ratio at the last point
|
||||
for (size_t i = 1; i < travel.size(); ++i) {
|
||||
if (i == 1) {
|
||||
// Lift to normal z at beginning
|
||||
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
|
||||
} else if (z != DBL_MAX && i == travel.size() - 1) {
|
||||
// Apply z_ratio for the very last point
|
||||
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
|
||||
} else {
|
||||
// For all points in between, no z change
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->set_last_pos(travel.points.back());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue