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:
parent
ab1b0e0ebc
commit
924a2b4551
16 changed files with 533 additions and 54 deletions
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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(); }
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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(); }
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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*/)
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in a new issue