ENH: Merge Scarf seam method from orca

Jira:none

BBL:Merged scarf seam and
add imorovemnt to improve seam on defalue setting
--as default setting wipe before rectract get good seam on end
--clip slope at start and end
--clip more on inner wall slope start than outer wall
--slowdown slope speed to get better sea
--todo-slope path had error double dE
  reduce it could improve seam,but slope could not stick well.
--check slope path overhang and avoid it while on conditional scarf mode

Co-authored-by: qing.zhang <qing.zhang@BambuLab.com>

Change-Id: I583a1c25e534b2aa5c9d710dcf207aefbea64347
This commit is contained in:
Noisyfox 2024-01-25 20:07:17 +08:00 committed by Lane.Wei
parent 42f095fa49
commit ad89f63fce
17 changed files with 748 additions and 41 deletions

View File

@ -7,10 +7,13 @@
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <sstream> #include <sstream>
#include "Utils.hpp"
#define L(s) (s) #define L(s) (s)
namespace Slic3r { namespace Slic3r {
static const double slope_path_ratio = 0.3;
static const double slope_inner_outer_wall_gap = 0.4;
void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{ {
@ -306,6 +309,89 @@ void ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
} }
} }
// BBS: clipe slope a bit
void ExtrusionLoopSloped::clip_slope(double distance, bool inter_perimeter)
{
this->clip_end(distance);
this->clip_front(distance*2);
}
// BBS
void ExtrusionLoopSloped::clip_end(const double distance)
{
double clip_dist = distance;
std::vector<ExtrusionPathSloped> &ends_slope = this->ends;
while (clip_dist > 0) {
ExtrusionPathSloped &last_path = ends_slope.back();
double len = last_path.length();
if (len <= clip_dist) {
ends_slope.pop_back();
clip_dist -= len;
} else {
last_path.polyline.clip_end(clip_dist);
break;
}
}
}
// BBS
void ExtrusionLoopSloped::clip_front(const double distance)
{
double clip_dist = distance;
if (this->role() == erPerimeter)
clip_dist = scale_(this->slope_path_length()) * slope_inner_outer_wall_gap;
std::vector<ExtrusionPathSloped> &start_slope = this->starts;
Polyline front_inward;
while (distance > 0) {
ExtrusionPathSloped &first_path = start_slope.front();
double len = first_path.length();
if (len <= clip_dist) {
start_slope.erase(start_slope.begin());
clip_dist -= len;
} else {
first_path.polyline.reverse();
first_path.polyline.clip_end(clip_dist);
first_path.polyline.reverse();
break;
}
}
}
double ExtrusionLoopSloped::slope_path_length() {
double total_length = 0.0;
for (ExtrusionPathSloped start_ep : this->starts) {
total_length += unscale_(start_ep.length());
}
return total_length;
}
// BBS: slowdown slope path seep to get better seam
void ExtrusionLoopSloped::slowdown_slope_speed() {
double speed_base = slope_path_ratio * this->target_speed;
double speed_update = speed_base;
double count_length = 0.0;
double total_length = this->slope_path_length();
for (ExtrusionPathSloped &start_ep : this->starts) {
start_ep.slope_begin.speed_record = speed_update;
count_length += unscale_(start_ep.length());
// mapping speed for each path
start_ep.slope_end.speed_record = speed_base + (this->target_speed - speed_base) * (count_length / total_length);
speed_update = start_ep.slope_end.speed_record;
}
for (size_t ep_index = 0; ep_index < this->ends.size(); ++ep_index) {
ExtrusionPathSloped &end_ep = this->ends[ep_index];
ExtrusionPathSloped &start_ep = this->starts[this->starts.size() - 1 - ep_index];
end_ep.slope_begin.speed_record = start_ep.slope_end.speed_record;
end_ep.slope_end.speed_record = start_ep.slope_begin.speed_record;
}
}
bool ExtrusionLoop::has_overhang_point(const Point &point) const bool ExtrusionLoop::has_overhang_point(const Point &point) const
{ {
for (const ExtrusionPath &path : this->paths) { for (const ExtrusionPath &path : this->paths) {
@ -339,6 +425,158 @@ double ExtrusionLoop::min_mm3_per_mm() const
return min_mm3_per_mm; return min_mm3_per_mm;
} }
// Orca: This function is used to check if the loop is smooth(continuous) or not.
// TODO: the main logic is largly copied from the calculate_polygon_angles_at_vertices function in SeamPlacer file. Need to refactor the code in the future.
bool ExtrusionLoop::is_smooth(double angle_threshold, double min_arm_length) const
{
// go through all the points in the loop and check if the angle between two segments(AB and BC) is less than the threshold
size_t idx_prev = 0;
size_t idx_curr = 0;
size_t idx_next = 0;
float distance_to_prev = 0;
float distance_to_next = 0;
const auto _polygon = polygon();
const Points &points = _polygon.points;
std::vector<float> lengths{};
for (size_t point_idx = 0; point_idx < points.size() - 1; ++point_idx) { lengths.push_back((unscale(points[point_idx]) - unscale(points[point_idx + 1])).norm()); }
lengths.push_back(std::max((unscale(points[0]) - unscale(points[points.size() - 1])).norm(), 0.1));
// push idx_prev far enough back as initialization
while (distance_to_prev < min_arm_length) {
idx_prev = Slic3r::prev_idx_modulo(idx_prev, points.size());
distance_to_prev += lengths[idx_prev];
}
for (size_t _i = 0; _i < points.size(); ++_i) {
// pull idx_prev to current as much as possible, while respecting the min_arm_length
while (distance_to_prev - lengths[idx_prev] > min_arm_length) {
distance_to_prev -= lengths[idx_prev];
idx_prev = Slic3r::next_idx_modulo(idx_prev, points.size());
}
// push idx_next forward as far as needed
while (distance_to_next < min_arm_length) {
distance_to_next += lengths[idx_next];
idx_next = Slic3r::next_idx_modulo(idx_next, points.size());
}
// Calculate angle between idx_prev, idx_curr, idx_next.
const Point &p0 = points[idx_prev];
const Point &p1 = points[idx_curr];
const Point &p2 = points[idx_next];
const auto a = angle(p0 - p1, p2 - p1);
if (a > 0 ? a < angle_threshold : a > -angle_threshold) { return false; }
// increase idx_curr by one
float curr_distance = lengths[idx_curr];
idx_curr++;
distance_to_prev += curr_distance;
distance_to_next -= curr_distance;
}
return true;
}
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

@ -46,6 +46,7 @@ enum ExtrusionLoopRole {
elrDefault, elrDefault,
elrContourInternalPerimeter, elrContourInternalPerimeter,
elrSkirt, elrSkirt,
elrPerimeterHole,
}; };
@ -285,6 +286,36 @@ 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.};
double speed_record{0.0};
};
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),
lerp(slope_begin.speed_record, slope_end.speed_record, 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:
@ -427,7 +458,8 @@ public:
append(dst, p.polyline.points); append(dst, p.polyline.points);
} }
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
// check if the loop is smooth, angle_threshold is in radians, default is 10 degrees
bool is_smooth(double angle_threshold = 0.174, double min_arm_length = 0.025) const;
//static inline std::string role_to_string(ExtrusionLoopRole role); //static inline std::string role_to_string(ExtrusionLoopRole role);
#ifndef NDEBUG #ifndef NDEBUG
@ -443,6 +475,24 @@ private:
ExtrusionLoopRole m_loop_role; ExtrusionLoopRole m_loop_role;
}; };
class ExtrusionLoopSloped : public ExtrusionLoop
{
public:
std::vector<ExtrusionPathSloped> starts;
std::vector<ExtrusionPathSloped> ends;
double target_speed{0.0};
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;
void clip_slope(double distance, bool inter_perimeter = false );
void clip_end(const double distance);
void clip_front(const double distance);
double slope_path_length();
void slowdown_slope_speed();
};
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

@ -3914,6 +3914,68 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
return out; return out;
} }
static bool has_overhang_path_on_slope(const ExtrusionLoop &loop, double slope_length)
{
double count_length = 0.0;
for (ExtrusionPath path : loop.paths) {
if (count_length > slope_length)
return false;
if (path.overhang_degree > 1)
return true;
count_length += path.length();
}
return false;
}
double GCode::get_path_speed(const ExtrusionPath &path)
{
double min_speed = double(m_config.slow_down_min_speed.get_at(m_writer.extruder()->id()));
// set speed
double speed = 0;
if (path.role() == erPerimeter) {
speed = m_config.get_abs_value("inner_wall_speed");
if (m_config.enable_overhang_speed.value) {
double new_speed = 0;
new_speed = get_overhang_degree_corr_speed(speed, path.overhang_degree);
speed = new_speed == 0.0 ? speed : new_speed;
}
} else if (path.role() == erExternalPerimeter) {
speed = m_config.get_abs_value("outer_wall_speed");
if (m_config.enable_overhang_speed.value) {
double new_speed = 0;
new_speed = get_overhang_degree_corr_speed(speed, path.overhang_degree);
speed = new_speed == 0.0 ? speed : new_speed;
}
} else if (path.role() == erOverhangPerimeter || path.role() == erBridgeInfill || path.role() == erSupportTransition) {
speed = m_config.get_abs_value("bridge_speed");
}
auto _mm3_per_mm = path.mm3_per_mm * double(m_curr_print->calib_mode() == CalibMode::Calib_Flow_Rate ? this->config().print_flow_ratio.value : 1);
// BBS: if not set the speed, then use the filament_max_volumetric_speed directly
if (speed == 0) {
if (_mm3_per_mm > 0)
speed = EXTRUDER_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm;
else
speed = EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm;
}
if (this->on_first_layer()) {
// BBS: for solid infill of initial layer, speed can be higher as long as
// wall lines have be attached
if (path.role() != erBottomSurface) speed = m_config.get_abs_value("initial_layer_speed");
}
if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) {
double extrude_speed = EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm;
if (_mm3_per_mm > 0) extrude_speed = EXTRUDER_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm;
// cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
speed = std::min(speed, extrude_speed);
}
return speed;
}
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed) std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed)
{ {
@ -3922,7 +3984,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// extrude all loops ccw // extrude all loops ccw
bool was_clockwise = loop.make_counter_clockwise(); bool was_clockwise = loop.make_counter_clockwise();
bool is_hole = loop.loop_role() == elrPerimeterHole;
// find the point of the loop that is closest to the current extruder position // find the point of the loop that is closest to the current extruder position
// or randomize if requested // or randomize if requested
Point last_pos = this->last_pos(); Point last_pos = this->last_pos();
@ -3933,13 +3995,24 @@ 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;
// BBS: not apply on fist layer, too small E has stick issue with hotend plate
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)) &&
!on_first_layer();
if (enable_seam_slope && m_config.seam_slope_conditional.value) {
enable_seam_slope = loop.is_smooth(m_config.scarf_angle_threshold.value * M_PI / 180., EXTRUDER_CONFIG(nozzle_diameter));
}
// 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_(EXTRUDER_CONFIG(nozzle_diameter)) * (m_config.seam_gap.value / 100);
scale_(EXTRUDER_CONFIG(nozzle_diameter)) * ( m_config.seam_gap.value / 100 ) : const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0;
0;
// get paths // get paths
ExtrusionPaths paths; ExtrusionPaths paths;
loop.clip_end(clip_length, &paths); loop.clip_end(clip_length, &paths);
@ -3953,12 +4026,70 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// extrude along the path // extrude along the path
std::string gcode; std::string gcode;
bool is_small_peri=false; bool is_small_peri=false;
const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath &path) {
// don't apply small perimeter setting for overhangs/bridges/non-perimeters
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;
};
//BBS: avoid overhang on conditional scarf mode
bool slope_has_overhang = false;
if (enable_seam_slope) {
// 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);
// BBS: check if has overhang on slope path
slope_has_overhang = has_overhang_path_on_slope(loop.paths, slope_min_length);
if (!slope_has_overhang) {
// Calculate the sloped loop
//BBS: should has smaller e at start to get better seam
ExtrusionLoopSloped new_loop(paths, seam_gap, slope_min_length, slope_max_segment_length, start_slope_ratio, loop.loop_role());
//BBS: clip end and start to get better seam
new_loop.clip_slope(seam_gap);
// BBS: slowdown speed to improve seam
new_loop.target_speed = get_path_speed(new_loop.starts.back());
new_loop.slowdown_slope_speed();
// 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());
}
}
}
if (!enable_seam_slope || slope_has_overhang) {
if (enable_seam_slope)
paths.back().clip_end(seam_gap);
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
// description += ExtrusionLoop::role_to_string(loop.loop_role()); gcode += this->_extrude(*path, description, speed_for_path(*path));
// description += ExtrusionEntity::role_to_string(path->role); }
//BBS: Small perimeter has been considered in curva and overhang detection in speed generater.
is_small_peri=(small_peri_speed>0 && is_perimeter(path->role()) && !is_bridge(path->role()) && path->get_overhang_degree()==0);
gcode += this->_extrude(*path, description, is_small_peri?small_peri_speed:speed);
} }
//BBS: don't reset acceleration when printing first layer. During first layer, acceleration is always same value. //BBS: don't reset acceleration when printing first layer. During first layer, acceleration is always same value.
@ -4271,21 +4402,28 @@ double GCode::get_overhang_degree_corr_speed(float normal_speed, double path_deg
return speed_out; return speed_out;
} }
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed) std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed, bool is_first_slope)
{ {
std::string gcode; std::string gcode;
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;
} }
@ -4499,7 +4637,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} }
// F is mm per minute. // F is mm per minute.
if (sloped == nullptr)
gcode += m_writer.set_speed(F, "", comment); gcode += m_writer.set_speed(F, "", comment);
double path_length = 0.; double path_length = 0.;
{ {
std::string comment = GCodeWriter::full_gcode_comment ? description : ""; std::string comment = GCodeWriter::full_gcode_comment ? description : "";
@ -4507,14 +4647,30 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
//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 || if (!m_config.enable_arc_fitting ||
path.polyline.fitting_result.empty() || path.polyline.fitting_result.empty() ||
m_config.spiral_mode) { m_config.spiral_mode ||
for (const Line& line : path.polyline.lines()) { sloped != nullptr) {
double path_length = 0.;
double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR;
for (const Line &line : path.polyline.lines()) {
const double line_length = line.length() * SCALING_FACTOR; const double line_length = line.length() * SCALING_FACTOR;
path_length += line_length; path_length += line_length;
if (sloped == nullptr) {
gcode += m_writer.extrude_to_xy( gcode += m_writer.extrude_to_xy(
this->point_to_gcode(line.b), this->point_to_gcode(line.b),
e_per_mm * line_length, e_per_mm * line_length,
comment); comment);
} else {
// Sloped extrusion
auto dE = e_per_mm * line_length;
auto [z_ratio, e_ratio, slope_speed] = sloped->interpolate(path_length / total_length);
gcode += m_writer.set_speed(slope_speed * 60, "", comment);
Vec2d dest2d = this->point_to_gcode(line.b);
Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio));
//BBS: todo, should use small e at start to get good seam
double slope_e = dE * e_ratio;
gcode += m_writer.extrude_to_xyz(dest3d, slope_e);
}
} }
} 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
@ -4613,7 +4769,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 )
{ {
/* 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
@ -4691,6 +4847,37 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment); gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
} }
} }
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);
} 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);
} else {
// 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);
} 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);
} else {
// For all points in between, no z change
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment );
}
}
}
}
this->set_last_pos(travel.points.back()); this->set_last_pos(travel.points.back());
} }

View File

@ -410,7 +410,7 @@ private:
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing); std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing);
std::string extrude_support(const ExtrusionEntityCollection &support_fills); std::string extrude_support(const ExtrusionEntityCollection &support_fills);
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);
// BBS // BBS
LiftType to_lift_type(ZHopType z_hop_types); LiftType to_lift_type(ZHopType z_hop_types);
@ -512,7 +512,8 @@ private:
// BBS // BBS
int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const; int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const;
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1); std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1, bool is_first_slope = false);
double get_path_speed(const ExtrusionPath &path);
double get_overhang_degree_corr_speed(float speed, double path_degree); double get_overhang_degree_corr_speed(float speed, double path_degree);
void print_machine_envelope(GCodeOutputStream &file, Print &print); void print_machine_envelope(GCodeOutputStream &file, Print &print);
void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);

View File

@ -1004,8 +1004,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;
} }
@ -1286,7 +1289,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)
@ -1363,8 +1370,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();
m_height_compare.reset(); m_height_compare.reset();
@ -2222,12 +2230,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
@ -3090,6 +3098,15 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
//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 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());
m_seams_detector.set_first_vertex(real_first_pos - m_extruder_offsets[m_extruder_id]); m_seams_detector.set_first_vertex(real_first_pos - m_extruder_offsets[m_extruder_id]);
} else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && m_detect_layer_based_on_tag) {
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 = real_last_pos - m_extruder_offsets[m_extruder_id];
// 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()) {
@ -3119,7 +3136,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
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 (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] > 0.0)
// 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]); m_result.spiral_vase_layers.back().first = static_cast<float>(m_end_position[Z]);
@ -3508,6 +3525,13 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
//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.has_first_vertex()) {
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);
} else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && m_detect_layer_based_on_tag) {
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 = real_last_pos - m_extruder_offsets[m_extruder_id];
// 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()) {
@ -4132,6 +4156,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

@ -693,7 +693,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
@ -762,6 +763,11 @@ 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);

View File

@ -178,7 +178,15 @@ void Layer::make_perimeters()
&& config.infill_wall_overlap == other_config.infill_wall_overlap && config.infill_wall_overlap == other_config.infill_wall_overlap
&& 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.seam_slope_type == other_config.seam_slope_type
&& config.seam_slope_conditional == other_config.seam_slope_conditional
&& config.scarf_angle_threshold == other_config.scarf_angle_threshold
&& 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

@ -416,7 +416,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
// there's only one contour loop). // there's only one contour loop).
loop_role = elrContourInternalPerimeter; loop_role = elrContourInternalPerimeter;
} else { } else {
loop_role = elrDefault; loop_role = loop.is_contour? elrDefault: elrPerimeterHole;
} }
// detect overhanging/bridging perimeters // detect overhanging/bridging perimeters
@ -961,7 +961,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
// Append paths to collection. // Append paths to collection.
if (!paths.empty()) { if (!paths.empty()) {
if (extrusion->is_closed) { if (extrusion->is_closed) {
ExtrusionLoop extrusion_loop(std::move(paths)); ExtrusionLoop extrusion_loop(std::move(paths), extrusion->is_contour()? elrDefault : elrPerimeterHole);
// Restore the orientation of the extrusion loop. // Restore the orientation of the extrusion loop.
if (pg_extrusion.is_contour) if (pg_extrusion.is_contour)
extrusion_loop.make_counter_clockwise(); extrusion_loop.make_counter_clockwise();

View File

@ -297,6 +297,51 @@ 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

@ -120,7 +120,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

@ -845,8 +845,8 @@ static std::vector<std::string> s_Preset_print_options {
// calib // calib
"print_flow_ratio", "print_flow_ratio",
//Orca //Orca
"exclude_object" "exclude_object", "seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "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 {
/*"filament_colour", */ "default_filament_colour","required_nozzle_HRC","filament_diameter", "filament_type", "filament_soluble", "filament_is_support", /*"filament_colour", */ "default_filament_colour","required_nozzle_HRC","filament_diameter", "filament_type", "filament_soluble", "filament_is_support",

View File

@ -1042,6 +1042,26 @@ 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

@ -233,6 +233,15 @@ 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)
static const t_config_enum_values s_keys_map_SLADisplayOrientation = { static const t_config_enum_values s_keys_map_SLADisplayOrientation = {
{ "landscape", sladoLandscape}, { "landscape", sladoLandscape},
{ "portrait", sladoPortrait} { "portrait", sladoPortrait}
@ -2227,6 +2236,10 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("")); def->set_default_value(new ConfigOptionString(""));
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;
@ -2799,6 +2812,70 @@ void PrintConfigDef::init_fff_params()
def->mode = comDevelop; def->mode = comDevelop;
def->set_default_value(new ConfigOptionPercent(15)); def->set_default_value(new ConfigOptionPercent(15));
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::All));
def = this->add("seam_slope_conditional", coBool);
def->label = L("Conditional scarf joint");
def->tooltip = L("Apply scarf joints only to smooth perimeters where traditional seams do not conceal the seams at sharp corners effectively.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("scarf_angle_threshold", coInt);
def->label = L("Conditional angle threshold");
def->tooltip = L("This option sets the threshold angle for applying a conditional scarf joint seam.\nIf the maximum angle within the perimeter loop " "exceeds this value (indicating the absence of sharp corners), a scarf joint seam will be used. The default value is 155°.");
def->mode = comAdvanced;
def->sidetext = L("°");
def->min = 0;
def->max = 180;
def->set_default_value(new ConfigOptionInt(155));
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.1, 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(10));
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(true));
def = this->add("wipe_speed", coPercent); def = this->add("wipe_speed", coPercent);
def->label = L("Wipe speed"); def->label = L("Wipe speed");
def->tooltip = L("The wipe speed is determined by the speed setting specified in this configuration." "If the value is expressed as a percentage (e.g. 80%), it will be calculated based on the travel speed setting above." "The default value for this parameter is 80%"); def->tooltip = L("The wipe speed is determined by the speed setting specified in this configuration." "If the value is expressed as a percentage (e.g. 80%), it will be calculated based on the travel speed setting above." "The default value for this parameter is 80%");

View File

@ -137,6 +137,13 @@ enum SeamPosition {
spNearest, spAligned, spRear, spRandom spNearest, spAligned, spRear, spRandom
}; };
// Orca
enum class SeamScarfType {
None,
External,
All,
};
enum SLAMaterial { enum SLAMaterial {
slamTough, slamTough,
slamFlex, slamFlex,
@ -330,6 +337,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)
@ -821,7 +829,17 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, initial_layer_flow_ratio)) ((ConfigOptionFloat, initial_layer_flow_ratio))
((ConfigOptionFloat, filter_out_gap_fill)) ((ConfigOptionFloat, filter_out_gap_fill))
//calib //calib
((ConfigOptionFloat, print_flow_ratio))) ((ConfigOptionFloat, print_flow_ratio))
// Orca: seam slopes
((ConfigOptionEnum<SeamScarfType>, seam_slope_type))
((ConfigOptionBool, seam_slope_conditional))
((ConfigOptionInt, scarf_angle_threshold))
((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(
MachineEnvelopeConfig, MachineEnvelopeConfig,
@ -926,6 +944,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, accel_to_decel_enable)) ((ConfigOptionBool, accel_to_decel_enable))
((ConfigOptionPercent, accel_to_decel_factor)) ((ConfigOptionPercent, accel_to_decel_factor))
((ConfigOptionEnumsGeneric, extruder_type)) ((ConfigOptionEnumsGeneric, extruder_type))
//Orca
((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

@ -902,6 +902,14 @@ 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_conditional"
|| opt_key == "scarf_angle_threshold"
|| 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 == "seam_gap" || opt_key == "seam_gap"
|| opt_key == "wipe_speed" || opt_key == "wipe_speed"
|| opt_key == "support_speed" || opt_key == "support_speed"

View File

@ -724,6 +724,17 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable")); toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable"));
} }
toggle_line("exclude_object", gcflavor == gcfKlipper); toggle_line("exclude_object", gcflavor == gcfKlipper);
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_conditional", has_seam_slope);
toggle_line("scarf_angle_threshold", has_seam_slope && config->opt_bool("seam_slope_conditional"));
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

@ -1904,6 +1904,14 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Seam"), L"param_seam"); optgroup = page->new_optgroup(L("Seam"), L"param_seam");
optgroup->append_single_option_line("seam_position", "Seam"); optgroup->append_single_option_line("seam_position", "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_conditional");
optgroup->append_single_option_line("scarf_angle_threshold");
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("wipe_speed", "Seam"); optgroup->append_single_option_line("wipe_speed", "Seam");
optgroup = page->new_optgroup(L("Precision"), L"param_precision"); optgroup = page->new_optgroup(L("Precision"), L"param_precision");