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 05961f7c98803a42871b8df855bfff6a102936c4)

* 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:
Noisyfox 2024-03-02 23:25:02 +08:00 committed by GitHub
parent ab1b0e0ebc
commit 924a2b4551
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 533 additions and 54 deletions

View file

@ -340,6 +340,115 @@ double ExtrusionLoop::min_mm3_per_mm() const
return min_mm3_per_mm; return min_mm3_per_mm;
} }
ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths,
double seam_gap,
double slope_min_length,
double slope_max_segment_length,
double start_slope_ratio,
ExtrusionLoopRole role)
: ExtrusionLoop(role)
{
// create slopes
const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath& path, const Polyline& poly,
double ratio_begin, double ratio_end) {
if (poly.empty()) {
return;
}
// Ensure `slope_max_segment_length`
Polyline detailed_poly;
{
detailed_poly.append(poly.first_point());
// Recursively split the line into half until no longer than `slope_max_segment_length`
const std::function<void(const Line&)> handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line& line) {
if (line.length() <= slope_max_segment_length) {
detailed_poly.append(line.b);
} else {
// Then process left half
handle_line({line.a, line.midpoint()});
// Then process right half
handle_line({line.midpoint(), line.b});
}
};
for (const auto& l : poly.lines()) {
handle_line(l);
}
}
starts.emplace_back(detailed_poly, path, ExtrusionPathSloped::Slope{ratio_begin, ratio_begin},
ExtrusionPathSloped::Slope{ratio_end, ratio_end});
if (is_approx(ratio_end, 1.) && seam_gap > 0) {
// Remove the segments that has no extrusion
const auto seg_length = detailed_poly.length();
if (seg_length > seam_gap) {
// Split the segment and remove the last `seam_gap` bit
const Polyline orig = detailed_poly;
Polyline tmp;
orig.split_at_length(seg_length - seam_gap, &detailed_poly, &tmp);
ratio_end = lerp(ratio_begin, ratio_end, (seg_length - seam_gap) / seg_length);
assert(1. - ratio_end > EPSILON);
} else {
// Remove the entire segment
detailed_poly.clear();
}
}
if (!detailed_poly.empty()) {
ends.emplace_back(detailed_poly, path, ExtrusionPathSloped::Slope{1., 1. - ratio_begin},
ExtrusionPathSloped::Slope{1., 1. - ratio_end});
}
};
double remaining_length = slope_min_length;
ExtrusionPaths::iterator path = original_paths.begin();
double start_ratio = start_slope_ratio;
for (; path != original_paths.end() && remaining_length > 0; ++path) {
const double path_len = unscale_(path->length());
if (path_len > remaining_length) {
// Split current path into slope and non-slope part
Polyline slope_path;
Polyline flat_path;
path->polyline.split_at_length(scale_(remaining_length), &slope_path, &flat_path);
add_slop(*path, slope_path, start_ratio, 1);
start_ratio = 1;
paths.emplace_back(std::move(flat_path), *path);
remaining_length = 0;
} else {
remaining_length -= path_len;
const double end_ratio = lerp(1.0, start_slope_ratio, remaining_length / slope_min_length);
add_slop(*path, path->polyline, start_ratio, end_ratio);
start_ratio = end_ratio;
}
}
assert(remaining_length <= 0);
assert(start_ratio == 1.);
// Put remaining flat paths
paths.insert(paths.end(), path, original_paths.end());
}
std::vector<const ExtrusionPath*> ExtrusionLoopSloped::get_all_paths() const {
std::vector<const ExtrusionPath*> r;
r.reserve(starts.size() + paths.size() + ends.size());
for (const auto& p : starts) {
r.push_back(&p);
}
for (const auto& p : paths) {
r.push_back(&p);
}
for (const auto& p : ends) {
r.push_back(&p);
}
return r;
}
std::string ExtrusionEntity::role_to_string(ExtrusionRole role) std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
{ {

View file

@ -301,6 +301,42 @@ private:
bool m_no_extrusion = false; bool m_no_extrusion = false;
}; };
class ExtrusionPathSloped : public ExtrusionPath
{
public:
struct Slope
{
double z_ratio{1.};
double e_ratio{1.};
};
Slope slope_begin;
Slope slope_end;
ExtrusionPathSloped(const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
: ExtrusionPath(rhs), slope_begin(begin), slope_end(end)
{}
ExtrusionPathSloped(ExtrusionPath&& rhs, const Slope& begin, const Slope& end)
: ExtrusionPath(std::move(rhs)), slope_begin(begin), slope_end(end)
{}
ExtrusionPathSloped(const Polyline& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
: ExtrusionPath(polyline, rhs), slope_begin(begin), slope_end(end)
{}
ExtrusionPathSloped(Polyline&& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
: ExtrusionPath(std::move(polyline), rhs), slope_begin(begin), slope_end(end)
{}
Slope interpolate(const double ratio) const
{
return {
lerp(slope_begin.z_ratio, slope_end.z_ratio, ratio),
lerp(slope_begin.e_ratio, slope_end.e_ratio, ratio),
};
}
bool is_flat() const { return is_approx(slope_begin.z_ratio, slope_end.z_ratio); }
};
class ExtrusionPathOriented : public ExtrusionPath class ExtrusionPathOriented : public ExtrusionPath
{ {
public: public:
@ -459,6 +495,22 @@ private:
ExtrusionLoopRole m_loop_role; ExtrusionLoopRole m_loop_role;
}; };
class ExtrusionLoopSloped : public ExtrusionLoop
{
public:
std::vector<ExtrusionPathSloped> starts;
std::vector<ExtrusionPathSloped> ends;
ExtrusionLoopSloped(ExtrusionPaths& original_paths,
double seam_gap,
double slope_min_length,
double slope_max_segment_length,
double start_slope_ratio,
ExtrusionLoopRole role = elrDefault);
[[nodiscard]] std::vector<const ExtrusionPath*> get_all_paths() const;
};
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{ {
dst.reserve(dst.size() + polylines.size()); dst.reserve(dst.size() + polylines.size());

View file

@ -4534,7 +4534,6 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
return out; return out;
} }
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters) 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 // 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 } else
loop.split_at(last_pos, false); 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; // 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 // if polyline was shorter than the clipping distance we'd get a null polyline, so
// we discard it in that case // we discard it in that case
double clip_length = m_enable_loop_clipping ? const double seam_gap = scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter)));
scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter))) : 0; const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0;
// get paths // get paths
ExtrusionPaths paths; ExtrusionPaths paths;
@ -4651,14 +4656,53 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
} }
const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath& path) {
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);
// don't apply small perimeter setting for overhangs/bridges/non-perimeters // 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); 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);
gcode += this->_extrude(*path, description, is_small_peri ? small_peri_speed : speed); 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 // BBS
@ -4932,14 +4976,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (is_bridge(path.role())) if (is_bridge(path.role()))
description += " (bridge)"; 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 // 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. //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 //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( gcode += this->travel_to(
path.first_point(), path.first_point(),
path.role(), 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; m_need_change_layer_lift_z = false;
} }
@ -5290,7 +5342,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (!variable_speed) { if (!variable_speed) {
// F is mm per minute. // F is mm per minute.
gcode += m_writer.set_speed(F, "", comment); gcode += m_writer.set_speed(F, "", comment);
double path_length = 0.;
{ {
if (m_enable_cooling_markers) { if (m_enable_cooling_markers) {
if (enable_overhang_bridge_fan) { 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 // 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()) { for (const Line& line : path.polyline.lines()) {
std::string tempDescription = description; std::string tempDescription = description;
const double line_length = line.length() * SCALING_FACTOR; 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); tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
} }
} }
gcode += m_writer.extrude_to_xy( if (sloped == nullptr) {
this->point_to_gcode(line.b), // Normal extrusion
dE, gcode += m_writer.extrude_to_xy(
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); 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 { } else {
// BBS: start to generate gcode from arc fitting data which includes line and arc // 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++) { 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 Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
const double line_length = line.length() * SCALING_FACTOR; const double line_length = line.length() * SCALING_FACTOR;
path_length += line_length;
auto dE = e_per_mm * line_length; auto dE = e_per_mm * line_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) { if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE; 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 ArcSegment& arc = fitting_result[fitting_index].arc_data;
const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR; 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); 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; auto dE = e_per_mm * arc_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) { if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE; auto oldE = dE;
@ -5407,6 +5470,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} else { } else {
double last_set_speed = new_points[0].speed * 60.0; 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); gcode += m_writer.set_speed(last_set_speed, "", comment);
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
bool pre_fan_enabled = false; 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) if( m_enable_cooling_markers && enable_overhang_bridge_fan)
pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role()); 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++) { for (size_t i = 1; i < new_points.size(); i++) {
std::string tempDescription = description; std::string tempDescription = description;
const ProcessedPoint &processed_point = new_points[i]; 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(); const double line_length = (p - prev).norm();
path_length += line_length;
double new_speed = pre_processed_point.speed * 60.0; double new_speed = pre_processed_point.speed * 60.0;
if (last_set_speed != new_speed) { if (last_set_speed != new_speed) {
gcode += m_writer.set_speed(new_speed, "", comment); 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); tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
} }
} }
gcode += if (sloped == nullptr) {
m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : ""); // 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; 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. // 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. /* 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 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) // use G1 because we rely on paths being straight (G0 may make round paths)
if (travel.size() >= 2) { if (travel.size() >= 2) {
for (size_t i = 1; i < travel.size(); ++ i) { if (m_spiral_vase) {
// BBS. Process lazy layer change, but don't do lazy layer change when enable spiral vase // No lazy z lift for spiral vase mode
Vec3d curr_pos = m_writer.get_position(); for (size_t i = 1; i < travel.size(); ++i) {
if (i == 1 && !m_spiral_vase) { gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy");
Vec2d dest2d = this->point_to_gcode(travel.points[i]); }
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z); } else {
gcode += m_writer.travel_to_xyz(dest3d, comment+" travel_to_xyz"); 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 { } 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()); this->set_last_pos(travel.points.back());

View file

@ -29,6 +29,7 @@
#include <map> #include <map>
#include <set> #include <set>
#include <string> #include <string>
#include <cfloat>
namespace Slic3r { namespace Slic3r {
@ -219,7 +220,7 @@ public:
void set_layer_count(unsigned int value) { m_layer_count = value; } void set_layer_count(unsigned int value) { m_layer_count = value; }
void apply_print_config(const PrintConfig &print_config); void apply_print_config(const PrintConfig &print_config);
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment); std::string travel_to(const Point& point, ExtrusionRole role, std::string comment, double z = DBL_MAX);
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type); bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift); std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }

View file

@ -1080,7 +1080,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode"); const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
if (spiral_vase != nullptr) if (spiral_vase != nullptr)
m_spiral_vase_active = spiral_vase->value; m_detect_layer_based_on_tag = spiral_vase->value;
const ConfigOptionBool* has_scarf_joint_seam = config.option<ConfigOptionBool>("has_scarf_joint_seam");
if (has_scarf_joint_seam != nullptr)
m_detect_layer_based_on_tag = m_detect_layer_based_on_tag || has_scarf_joint_seam->value;
const ConfigOptionBool* manual_filament_change = config.option<ConfigOptionBool>("manual_filament_change"); const ConfigOptionBool* manual_filament_change = config.option<ConfigOptionBool>("manual_filament_change");
if (manual_filament_change != nullptr) if (manual_filament_change != nullptr)
@ -1397,7 +1401,11 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode"); const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
if (spiral_vase != nullptr) if (spiral_vase != nullptr)
m_spiral_vase_active = spiral_vase->value; m_detect_layer_based_on_tag = spiral_vase->value;
const ConfigOptionBool* has_scarf_joint_seam = config.option<ConfigOptionBool>("has_scarf_joint_seam");
if (has_scarf_joint_seam != nullptr)
m_detect_layer_based_on_tag = m_detect_layer_based_on_tag || has_scarf_joint_seam->value;
const ConfigOptionEnumGeneric *bed_type = config.option<ConfigOptionEnumGeneric>("curr_bed_type"); const ConfigOptionEnumGeneric *bed_type = config.option<ConfigOptionEnumGeneric>("curr_bed_type");
if (bed_type != nullptr) if (bed_type != nullptr)
@ -1479,7 +1487,9 @@ void GCodeProcessor::reset()
m_options_z_corrector.reset(); m_options_z_corrector.reset();
m_spiral_vase_active = false; m_detect_layer_based_on_tag = false;
m_seams_count = 0;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_mm3_per_mm_compare.reset(); m_mm3_per_mm_compare.reset();
@ -2344,12 +2354,12 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
// layer change tag // layer change tag
if (comment == reserved_tag(ETags::Layer_Change)) { if (comment == reserved_tag(ETags::Layer_Change)) {
++m_layer_id; ++m_layer_id;
if (m_spiral_vase_active) { if (m_detect_layer_based_on_tag) {
if (m_result.moves.empty() || m_result.spiral_vase_layers.empty()) if (m_result.moves.empty() || m_result.spiral_vase_layers.empty())
// add a placeholder for layer height. the actual value will be set inside process_G1() method // add a placeholder for layer height. the actual value will be set inside process_G1() method
m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } }); m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } });
else { else {
const size_t move_id = m_result.moves.size() - 1; const size_t move_id = m_result.moves.size() - 1 - m_seams_count;
if (!m_result.spiral_vase_layers.empty()) if (!m_result.spiral_vase_layers.empty())
m_result.spiral_vase_layers.back().second.second = move_id; m_result.spiral_vase_layers.back().second.second = move_id;
// add a placeholder for layer height. the actual value will be set inside process_G1() method // add a placeholder for layer height. the actual value will be set inside process_G1() method
@ -3215,12 +3225,22 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
machine.calculate_time(TimeProcessor::Planner::queue_size); machine.calculate_time(TimeProcessor::Planner::queue_size);
} }
const Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f};
if (m_seams_detector.is_active()) { if (m_seams_detector.is_active()) {
// check for seam starting vertex // check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) { if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
//BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
const Vec3f real_first_pos = Vec3f(m_result.moves.back().position.x() - m_x_offset, m_result.moves.back().position.y() - m_y_offset, m_result.moves.back().position.z()); const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
m_seams_detector.set_first_vertex(real_first_pos - m_extruder_offsets[m_extruder_id]); if (!m_seams_detector.has_first_vertex()) {
m_seams_detector.set_first_vertex(new_pos);
} else if (m_detect_layer_based_on_tag) {
// We may have sloped loop, drop any previous start pos if we have z increment
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
if (new_pos.z() > first_vertex->z()) {
m_seams_detector.set_first_vertex(new_pos);
}
}
} }
// check for seam ending vertex and store the resulting move // check for seam ending vertex and store the resulting move
else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) { else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) {
@ -3230,8 +3250,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
//BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
const Vec3f real_last_pos = Vec3f(m_result.moves.back().position.x() - m_x_offset, m_result.moves.back().position.y() - m_y_offset, m_result.moves.back().position.z()); const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
const Vec3f new_pos = real_last_pos - m_extruder_offsets[m_extruder_id];
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
// the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
@ -3246,16 +3265,21 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
} }
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.activate(true); m_seams_detector.activate(true);
Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f};
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
} }
if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty()) { if (m_detect_layer_based_on_tag && !m_result.spiral_vase_layers.empty()) {
if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] >= 0.0) if (delta_pos[Z] >= 0.0 && type == EMoveType::Extrude) {
const float current_z = static_cast<float>(m_end_position[Z]);
// replace layer height placeholder with correct value // replace layer height placeholder with correct value
m_result.spiral_vase_layers.back().first = static_cast<float>(m_end_position[Z]); if (m_result.spiral_vase_layers.back().first == FLT_MAX) {
m_result.spiral_vase_layers.back().first = current_z;
} else {
m_result.spiral_vase_layers.back().first = std::max(m_result.spiral_vase_layers.back().first, current_z);
}
}
if (!m_result.moves.empty()) if (!m_result.moves.empty())
m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1; m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1 - m_seams_count;
} }
// store move // store move
@ -3637,8 +3661,17 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
if (m_seams_detector.is_active()) { if (m_seams_detector.is_active()) {
//BBS: check for seam starting vertex //BBS: check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) { if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
if (!m_seams_detector.has_first_vertex()) {
m_seams_detector.set_first_vertex(new_pos);
} else if (m_detect_layer_based_on_tag) {
// We may have sloped loop, drop any previous start pos if we have z increment
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
if (new_pos.z() > first_vertex->z()) {
m_seams_detector.set_first_vertex(new_pos);
}
}
} }
//BBS: check for seam ending vertex and store the resulting move //BBS: check for seam ending vertex and store the resulting move
else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) { else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) {
@ -4267,6 +4300,10 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
m_interpolation_points, m_interpolation_points,
}); });
if (type == EMoveType::Seam) {
m_seams_count++;
}
// stores stop time placeholders for later use // stores stop time placeholders for later use
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) { if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {

View file

@ -700,7 +700,8 @@ namespace Slic3r {
SeamsDetector m_seams_detector; SeamsDetector m_seams_detector;
OptionsZCorrector m_options_z_corrector; OptionsZCorrector m_options_z_corrector;
size_t m_last_default_color_id; size_t m_last_default_color_id;
bool m_spiral_vase_active; bool m_detect_layer_based_on_tag {false};
int m_seams_count;
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time; std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -770,6 +771,10 @@ namespace Slic3r {
//BBS: set offset for gcode writer //BBS: set offset for gcode writer
void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; } void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; }
// Orca: if true, only change new layer if ETags::Layer_Change occurs
// otherwise when we got a lift of z during extrusion, a new layer will be added
void detect_layer_based_on_tag(bool enabled) { m_detect_layer_based_on_tag = enabled; }
private: private:
void apply_config(const DynamicPrintConfig& config); void apply_config(const DynamicPrintConfig& config);
void apply_config_simplify3d(const std::string& filename); void apply_config_simplify3d(const std::string& filename);

View file

@ -184,7 +184,13 @@ void Layer::make_perimeters()
&& config.fuzzy_skin == other_config.fuzzy_skin && config.fuzzy_skin == other_config.fuzzy_skin
&& config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness && config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness
&& config.fuzzy_skin_point_distance == other_config.fuzzy_skin_point_distance && config.fuzzy_skin_point_distance == other_config.fuzzy_skin_point_distance
&& config.fuzzy_skin_first_layer == other_config.fuzzy_skin_first_layer) && config.fuzzy_skin_first_layer == other_config.fuzzy_skin_first_layer
&& config.seam_slope_type == other_config.seam_slope_type
&& config.seam_slope_start_height == other_config.seam_slope_start_height
&& config.seam_slope_entire_loop == other_config.seam_slope_entire_loop
&& config.seam_slope_min_length == other_config.seam_slope_min_length
&& config.seam_slope_steps == other_config.seam_slope_steps
&& config.seam_slope_inner_walls == other_config.seam_slope_inner_walls)
{ {
other_layerm->perimeters.clear(); other_layerm->perimeters.clear();
other_layerm->fills.clear(); other_layerm->fills.clear();

View file

@ -309,6 +309,53 @@ bool Polyline::split_at_index(const size_t index, Polyline* p1, Polyline* p2) co
return true; return true;
} }
bool Polyline::split_at_length(const double length, Polyline* p1, Polyline* p2) const
{
if (this->points.empty()) return false;
if (length < 0 || length > this->length()) {
return false;
}
if (length < SCALED_EPSILON) {
p1->clear();
p1->append(this->first_point());
*p2 = *this;
} else if (is_approx(length, this->length(), SCALED_EPSILON)) {
p2->clear();
p2->append(this->last_point());
*p1 = *this;
} else {
// 1 find the line to split at
size_t line_idx = 0;
double acc_length = 0;
Point p = this->first_point();
for (const auto& l : this->lines()) {
p = l.b;
const double current_length = l.length();
if (acc_length + current_length >= length) {
p = lerp(l.a, l.b, (length - acc_length) / current_length);
break;
}
acc_length += current_length;
line_idx++;
}
//2 judge whether the cloest point is one vertex of polyline.
// and spilit the polyline at different index
int index = this->find_point(p);
if (index != -1) {
this->split_at_index(index, p1, p2);
} else {
Polyline temp;
this->split_at_index(line_idx, p1, &temp);
p1->append(p);
this->split_at_index(line_idx + 1, &temp, p2);
p2->append_before(p);
}
}
return true;
}
bool Polyline::is_straight() const bool Polyline::is_straight() const
{ {

View file

@ -130,6 +130,7 @@ public:
// template <class T> void simplify_by_visibility(const T &area); // template <class T> void simplify_by_visibility(const T &area);
void split_at(Point &point, Polyline* p1, Polyline* p2) const; void split_at(Point &point, Polyline* p1, Polyline* p2) const;
bool split_at_index(const size_t index, Polyline* p1, Polyline* p2) const; bool split_at_index(const size_t index, Polyline* p1, Polyline* p2) const;
bool split_at_length(const double length, Polyline* p1, Polyline* p2) const;
bool is_straight() const; bool is_straight() const;
bool is_closed() const { return this->points.front() == this->points.back(); } bool is_closed() const { return this->points.front() == this->points.back(); }

View file

@ -820,6 +820,7 @@ static std::vector<std::string> s_Preset_print_options {
"wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic",
"hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
"small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model",
"seam_slope_type", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls",
}; };
static std::vector<std::string> s_Preset_filament_options { static std::vector<std::string> s_Preset_filament_options {

View file

@ -1041,6 +1041,24 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
else else
m_support_used = false; m_support_used = false;
{
const auto& o = model.objects;
const auto opt_has_scarf_joint_seam = [](const DynamicConfig& c) {
return c.has("seam_slope_type") && c.opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None;
};
const bool has_scarf_joint_seam = std::any_of(o.begin(), o.end(), [&new_full_config, &opt_has_scarf_joint_seam](ModelObject* obj) {
return obj->get_config_value<ConfigOptionEnum<SeamScarfType>>(new_full_config, "seam_slope_type")->value != SeamScarfType::None ||
std::any_of(obj->volumes.begin(), obj->volumes.end(), [&opt_has_scarf_joint_seam](const ModelVolume* v) { return opt_has_scarf_joint_seam(v->config.get());}) ||
std::any_of(obj->layer_config_ranges.begin(), obj->layer_config_ranges.end(), [&opt_has_scarf_joint_seam](const auto& r) { return opt_has_scarf_joint_seam(r.second.get());});
});
if (has_scarf_joint_seam) {
new_full_config.set("has_scarf_joint_seam", true);
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", has_scarf_joint_seam:" << has_scarf_joint_seam;
}
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
DynamicPrintConfig filament_overrides; DynamicPrintConfig filament_overrides;
//BBS: add plate index //BBS: add plate index

View file

@ -264,6 +264,14 @@ static t_config_enum_values s_keys_map_SeamPosition {
}; };
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition) CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition)
// Orca
static t_config_enum_values s_keys_map_SeamScarfType{
{ "none", int(SeamScarfType::None) },
{ "external", int(SeamScarfType::External) },
{ "all", int(SeamScarfType::All) },
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamScarfType)
// Orca // Orca
static t_config_enum_values s_keys_map_InternalBridgeFilter { static t_config_enum_values s_keys_map_InternalBridgeFilter {
{ "disabled", ibfDisabled }, { "disabled", ibfDisabled },
@ -2795,6 +2803,10 @@ def = this->add("filament_loading_speed", coFloats);
def->height = 15; def->height = 15;
def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"}); def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"});
def = this->add("has_scarf_joint_seam", coBool);
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
{ {
struct AxisDefault { struct AxisDefault {
std::string name; std::string name;
@ -3523,6 +3535,55 @@ def = this->add("filament_loading_speed", coFloats);
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(10,true)); def->set_default_value(new ConfigOptionFloatOrPercent(10,true));
def = this->add("seam_slope_type", coEnum);
def->label = L("Scarf joint seam (beta)");
def->tooltip = L("Use scarf joint to minimize seam visibility and increase seam strength.");
def->enum_keys_map = &ConfigOptionEnum<SeamScarfType>::get_enum_values();
def->enum_values.push_back("none");
def->enum_values.push_back("external");
def->enum_values.push_back("all");
def->enum_labels.push_back(L("None"));
def->enum_labels.push_back(L("Contour"));
def->enum_labels.push_back(L("Contour and hole"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SeamScarfType>(SeamScarfType::None));
def = this->add("seam_slope_start_height", coFloatOrPercent);
def->label = L("Scarf start height");
def->tooltip = L("Start height of the scarf.\n"
"This amount can be specified in millimeters or as a percentage of the current layer height. The default value for this parameter is 0.");
def->sidetext = L("mm or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
def = this->add("seam_slope_entire_loop", coBool);
def->label = L("Scarf around entire wall");
def->tooltip = L("The scarf extends to the entire length of the wall.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("seam_slope_min_length", coFloat);
def->label = L("Scarf length");
def->tooltip = L("Length of the scarf. Setting this parameter to zero effectively disables the scarf.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(20));
def = this->add("seam_slope_steps", coInt);
def->label = L("Scarf steps");
def->tooltip = L("Minimum number of segments of each scarf.");
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(10));
def = this->add("seam_slope_inner_walls", coBool);
def->label = L("Scarf joint for inner walls");
def->tooltip = L("Use scarf joint for inner walls as well.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("role_based_wipe_speed", coBool); def = this->add("role_based_wipe_speed", coBool);
def->label = L("Role base wipe speed"); def->label = L("Role base wipe speed");
def->tooltip = L("The wipe speed is determined by the speed of the current extrusion role." def->tooltip = L("The wipe speed is determined by the speed of the current extrusion role."

View file

@ -169,6 +169,13 @@ enum SeamPosition {
spNearest, spAligned, spRear, spRandom spNearest, spAligned, spRear, spRandom
}; };
// Orca
enum class SeamScarfType {
None,
External,
All,
};
//Orca //Orca
enum InternalBridgeFilter { enum InternalBridgeFilter {
ibfDisabled, ibfLimited, ibfNofilter ibfDisabled, ibfLimited, ibfNofilter
@ -382,6 +389,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportMaterialInterfacePattern)
// BBS // BBS
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamPosition) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamPosition)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamScarfType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLADisplayOrientation) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLADisplayOrientation)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType)
@ -838,7 +846,6 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, top_surface_jerk)) ((ConfigOptionFloat, top_surface_jerk))
((ConfigOptionFloat, initial_layer_jerk)) ((ConfigOptionFloat, initial_layer_jerk))
((ConfigOptionFloat, travel_jerk)) ((ConfigOptionFloat, travel_jerk))
) )
// This object is mapped to Perl as Slic3r::Config::PrintRegion. // This object is mapped to Perl as Slic3r::Config::PrintRegion.
@ -943,6 +950,14 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, is_infill_first)) ((ConfigOptionBool, is_infill_first))
((ConfigOptionBool, small_area_infill_flow_compensation)) ((ConfigOptionBool, small_area_infill_flow_compensation))
((ConfigOptionEnum<WallDirection>, wall_direction)) ((ConfigOptionEnum<WallDirection>, wall_direction))
// Orca: seam slopes
((ConfigOptionEnum<SeamScarfType>, seam_slope_type))
((ConfigOptionFloatOrPercent, seam_slope_start_height))
((ConfigOptionBool, seam_slope_entire_loop))
((ConfigOptionFloat, seam_slope_min_length))
((ConfigOptionInt, seam_slope_steps))
((ConfigOptionBool, seam_slope_inner_walls))
) )
PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE(
@ -1092,6 +1107,8 @@ PRINT_CONFIG_CLASS_DEFINE(
// Small Area Infill Flow Compensation // Small Area Infill Flow Compensation
((ConfigOptionStrings, small_area_infill_flow_compensation_model)) ((ConfigOptionStrings, small_area_infill_flow_compensation_model))
((ConfigOptionBool, has_scarf_joint_seam))
) )
// This object is mapped to Perl as Slic3r::Config::Print. // This object is mapped to Perl as Slic3r::Config::Print.

View file

@ -1142,6 +1142,12 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posSlice); steps.emplace_back(posSlice);
} else if ( } else if (
opt_key == "seam_position" opt_key == "seam_position"
|| opt_key == "seam_slope_type"
|| opt_key == "seam_slope_start_height"
|| opt_key == "seam_slope_entire_loop"
|| opt_key == "seam_slope_min_length"
|| opt_key == "seam_slope_steps"
|| opt_key == "seam_slope_inner_walls"
|| opt_key == "support_speed" || opt_key == "support_speed"
|| opt_key == "support_interface_speed" || opt_key == "support_interface_speed"
|| opt_key == "overhang_1_4_speed" || opt_key == "overhang_1_4_speed"

View file

@ -752,6 +752,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation"); bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation");
toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation); toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation);
toggle_field("seam_slope_type", !has_spiral_vase);
bool has_seam_slope = !has_spiral_vase && config->opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None;
toggle_line("seam_slope_start_height", has_seam_slope);
toggle_line("seam_slope_entire_loop", has_seam_slope);
toggle_line("seam_slope_min_length", has_seam_slope);
toggle_line("seam_slope_steps", has_seam_slope);
toggle_line("seam_slope_inner_walls", has_seam_slope);
toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop"));
} }
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)

View file

@ -1976,6 +1976,12 @@ void TabPrint::build()
optgroup->append_single_option_line("seam_position", "seam"); optgroup->append_single_option_line("seam_position", "seam");
optgroup->append_single_option_line("staggered_inner_seams", "seam"); optgroup->append_single_option_line("staggered_inner_seams", "seam");
optgroup->append_single_option_line("seam_gap","seam"); optgroup->append_single_option_line("seam_gap","seam");
optgroup->append_single_option_line("seam_slope_type");
optgroup->append_single_option_line("seam_slope_start_height");
optgroup->append_single_option_line("seam_slope_entire_loop");
optgroup->append_single_option_line("seam_slope_min_length");
optgroup->append_single_option_line("seam_slope_steps");
optgroup->append_single_option_line("seam_slope_inner_walls");
optgroup->append_single_option_line("role_based_wipe_speed","seam"); optgroup->append_single_option_line("role_based_wipe_speed","seam");
optgroup->append_single_option_line("wipe_speed", "seam"); optgroup->append_single_option_line("wipe_speed", "seam");
optgroup->append_single_option_line("wipe_on_loops","seam"); optgroup->append_single_option_line("wipe_on_loops","seam");