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:
parent
42f095fa49
commit
ad89f63fce
|
@ -7,11 +7,14 @@
|
|||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include "Utils.hpp"
|
||||
|
||||
#define L(s) (s)
|
||||
|
||||
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
|
||||
{
|
||||
this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval);
|
||||
|
@ -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
|
||||
{
|
||||
for (const ExtrusionPath &path : this->paths) {
|
||||
|
@ -339,6 +425,158 @@ double ExtrusionLoop::min_mm3_per_mm() const
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -46,6 +46,7 @@ enum ExtrusionLoopRole {
|
|||
elrDefault,
|
||||
elrContourInternalPerimeter,
|
||||
elrSkirt,
|
||||
elrPerimeterHole,
|
||||
};
|
||||
|
||||
|
||||
|
@ -285,6 +286,36 @@ private:
|
|||
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
|
||||
{
|
||||
public:
|
||||
|
@ -427,7 +458,8 @@ public:
|
|||
append(dst, p.polyline.points);
|
||||
}
|
||||
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);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
@ -443,6 +475,24 @@ private:
|
|||
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)
|
||||
{
|
||||
dst.reserve(dst.size() + polylines.size());
|
||||
|
|
|
@ -3914,6 +3914,68 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
|
|||
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)
|
||||
{
|
||||
|
@ -3922,7 +3984,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
|
||||
// extrude all loops ccw
|
||||
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
|
||||
// or randomize if requested
|
||||
Point last_pos = this->last_pos();
|
||||
|
@ -3933,14 +3995,25 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
} else
|
||||
loop.split_at(last_pos, false);
|
||||
|
||||
const auto seam_scarf_type = m_config.seam_slope_type.value;
|
||||
// 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;
|
||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
// we discard it in that case
|
||||
double clip_length = m_enable_loop_clipping ?
|
||||
scale_(EXTRUDER_CONFIG(nozzle_diameter)) * ( m_config.seam_gap.value / 100 ) :
|
||||
0;
|
||||
|
||||
// get paths
|
||||
const double seam_gap = 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;
|
||||
// get paths
|
||||
ExtrusionPaths paths;
|
||||
loop.clip_end(clip_length, &paths);
|
||||
if (paths.empty()) return "";
|
||||
|
@ -3953,12 +4026,70 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
// extrude along the path
|
||||
std::string gcode;
|
||||
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);
|
||||
//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);
|
||||
|
||||
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) {
|
||||
gcode += this->_extrude(*path, description, speed_for_path(*path));
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (is_bridge(path.role()))
|
||||
description += " (bridge)";
|
||||
|
||||
const ExtrusionPathSloped *sloped = dynamic_cast<const ExtrusionPathSloped *>(&path);
|
||||
const auto get_sloped_z = [&sloped, this](double z_ratio) {
|
||||
const auto height = sloped->height;
|
||||
return lerp(m_nominal_z - height, m_nominal_z, z_ratio);
|
||||
};
|
||||
|
||||
// go to first point of extrusion path
|
||||
//BBS: path.first_point is 2D point. But in lazy raise case, lift z is done in travel_to function.
|
||||
//Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance
|
||||
if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z) {
|
||||
if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z || (sloped != nullptr && !sloped->is_flat())) {
|
||||
gcode += this->travel_to(
|
||||
path.first_point(),
|
||||
path.role(),
|
||||
"move to first " + description + " point"
|
||||
"move to first " + description + " point",
|
||||
sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio)
|
||||
);
|
||||
m_need_change_layer_lift_z = false;
|
||||
}
|
||||
|
@ -4499,7 +4637,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
}
|
||||
|
||||
// F is mm per minute.
|
||||
gcode += m_writer.set_speed(F, "", comment);
|
||||
if (sloped == nullptr)
|
||||
gcode += m_writer.set_speed(F, "", comment);
|
||||
|
||||
double path_length = 0.;
|
||||
{
|
||||
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
|
||||
if (!m_config.enable_arc_fitting ||
|
||||
path.polyline.fitting_result.empty() ||
|
||||
m_config.spiral_mode) {
|
||||
for (const Line& line : path.polyline.lines()) {
|
||||
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()) {
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
e_per_mm * line_length,
|
||||
comment);
|
||||
|
||||
if (sloped == nullptr) {
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
e_per_mm * line_length,
|
||||
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 {
|
||||
// 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.
|
||||
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.
|
||||
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);
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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_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
|
||||
LiftType to_lift_type(ZHopType z_hop_types);
|
||||
|
||||
|
@ -512,7 +512,8 @@ private:
|
|||
// BBS
|
||||
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);
|
||||
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);
|
||||
|
|
|
@ -1004,8 +1004,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
|
||||
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
|
||||
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");
|
||||
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");
|
||||
if (bed_type != nullptr)
|
||||
|
@ -1363,8 +1370,9 @@ void GCodeProcessor::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
|
||||
m_mm3_per_mm_compare.reset();
|
||||
m_height_compare.reset();
|
||||
|
@ -2222,12 +2230,12 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
// layer change tag
|
||||
if (comment == reserved_tag(ETags::Layer_Change)) {
|
||||
++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())
|
||||
// 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 } });
|
||||
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())
|
||||
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
|
||||
|
@ -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
|
||||
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]);
|
||||
} 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
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
// replace layer height placeholder with correct value
|
||||
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
|
||||
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);
|
||||
} 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
|
||||
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,
|
||||
});
|
||||
|
||||
if (type == EMoveType::Seam) {
|
||||
m_seams_count++;
|
||||
}
|
||||
|
||||
// stores stop time placeholders for later use
|
||||
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
|
|
|
@ -693,7 +693,8 @@ namespace Slic3r {
|
|||
SeamsDetector m_seams_detector;
|
||||
OptionsZCorrector m_options_z_corrector;
|
||||
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
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -762,6 +763,11 @@ namespace Slic3r {
|
|||
|
||||
//BBS: set offset for gcode writer
|
||||
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:
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
|
|
|
@ -178,7 +178,15 @@ void Layer::make_perimeters()
|
|||
&& config.infill_wall_overlap == other_config.infill_wall_overlap
|
||||
&& config.fuzzy_skin == other_config.fuzzy_skin
|
||||
&& 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->fills.clear();
|
||||
|
|
|
@ -416,7 +416,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
// there's only one contour loop).
|
||||
loop_role = elrContourInternalPerimeter;
|
||||
} else {
|
||||
loop_role = elrDefault;
|
||||
loop_role = loop.is_contour? elrDefault: elrPerimeterHole;
|
||||
}
|
||||
|
||||
// detect overhanging/bridging perimeters
|
||||
|
@ -961,7 +961,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
|||
// Append paths to collection.
|
||||
if (!paths.empty()) {
|
||||
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.
|
||||
if (pg_extrusion.is_contour)
|
||||
extrusion_loop.make_counter_clockwise();
|
||||
|
|
|
@ -297,6 +297,51 @@ bool Polyline::split_at_index(const size_t index, Polyline* p1, Polyline* p2) co
|
|||
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
|
||||
{
|
||||
|
|
|
@ -120,7 +120,7 @@ public:
|
|||
// template <class T> void simplify_by_visibility(const T &area);
|
||||
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_length(const double length, Polyline *p1, Polyline *p2) const;
|
||||
bool is_straight() const;
|
||||
bool is_closed() const { return this->points.front() == this->points.back(); }
|
||||
|
||||
|
|
|
@ -845,8 +845,8 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
// calib
|
||||
"print_flow_ratio",
|
||||
//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 {
|
||||
/*"filament_colour", */ "default_filament_colour","required_nozzle_HRC","filament_diameter", "filament_type", "filament_soluble", "filament_is_support",
|
||||
|
|
|
@ -1042,6 +1042,26 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
else
|
||||
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.
|
||||
DynamicPrintConfig filament_overrides;
|
||||
//BBS: add plate index
|
||||
|
|
|
@ -233,6 +233,15 @@ static t_config_enum_values s_keys_map_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 = {
|
||||
{ "landscape", sladoLandscape},
|
||||
{ "portrait", sladoPortrait}
|
||||
|
@ -2227,6 +2236,10 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
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 {
|
||||
std::string name;
|
||||
|
@ -2799,6 +2812,70 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comDevelop;
|
||||
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->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%");
|
||||
|
|
|
@ -137,6 +137,13 @@ enum SeamPosition {
|
|||
spNearest, spAligned, spRear, spRandom
|
||||
};
|
||||
|
||||
// Orca
|
||||
enum class SeamScarfType {
|
||||
None,
|
||||
External,
|
||||
All,
|
||||
};
|
||||
|
||||
enum SLAMaterial {
|
||||
slamTough,
|
||||
slamFlex,
|
||||
|
@ -330,6 +337,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportMaterialInterfacePattern)
|
|||
// BBS
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportType)
|
||||
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(SLAPillarConnectionMode)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType)
|
||||
|
@ -821,7 +829,17 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionFloat, initial_layer_flow_ratio))
|
||||
((ConfigOptionFloat, filter_out_gap_fill))
|
||||
//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(
|
||||
MachineEnvelopeConfig,
|
||||
|
@ -926,6 +944,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, accel_to_decel_enable))
|
||||
((ConfigOptionPercent, accel_to_decel_factor))
|
||||
((ConfigOptionEnumsGeneric, extruder_type))
|
||||
//Orca
|
||||
((ConfigOptionBool, has_scarf_joint_seam))
|
||||
)
|
||||
|
||||
// This object is mapped to Perl as Slic3r::Config::Print.
|
||||
|
|
|
@ -902,6 +902,14 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
steps.emplace_back(posSlice);
|
||||
} else if (
|
||||
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 == "wipe_speed"
|
||||
|| opt_key == "support_speed"
|
||||
|
|
|
@ -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_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*/)
|
||||
|
|
|
@ -1904,6 +1904,14 @@ void TabPrint::build()
|
|||
optgroup = page->new_optgroup(L("Seam"), L"param_seam");
|
||||
optgroup->append_single_option_line("seam_position", "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 = page->new_optgroup(L("Precision"), L"param_precision");
|
||||
|
|
Loading…
Reference in New Issue