diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 65182c3d9..3fc9e8bb5 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -7,11 +7,14 @@ #include #include #include +#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 &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 &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 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 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 ExtrusionLoopSloped::get_all_paths() const +{ + std::vector 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) { diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index c1b48fd26..f2c21e83a 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -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 starts; + std::vector 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 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()); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 972f62ca8..8517cf8d9 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3914,6 +3914,68 @@ static std::unique_ptr 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(&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 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()); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index dcbd3d144..79ee6dcbc 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -410,7 +410,7 @@ private: std::string extrude_infill(const Print &print, const std::vector &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); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 1dae839a2..4df452149 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1004,8 +1004,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config) const ConfigOptionBool* spiral_vase = config.option("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("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("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("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("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 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(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 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(PrintEstimatedStatistics::ETimeMode::Count); ++i) { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 39634b4a7..a0082ccd7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -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 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); diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index d8a86aa9a..026530198 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -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(); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 26d97a2d4..7dc4c613b 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -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(); diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 383e36604..783f91229 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -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 { diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index ccd89bb2f..320a98fef 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -120,7 +120,7 @@ public: // template 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(); } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index db0287032..67f6a62ea 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -845,8 +845,8 @@ static std::vector 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 s_Preset_filament_options { /*"filament_colour", */ "default_filament_colour","required_nozzle_HRC","filament_diameter", "filament_type", "filament_soluble", "filament_is_support", diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index a16653f03..54aa20a00 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -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("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>(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 diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 368b2b2fa..94ebd8e54 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -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::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::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%"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index fc7a0103b..2711db044 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -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, 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. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0b13215a2..8e19621ca 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -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" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 8a453eeb0..fbc444a4d 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -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("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*/) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a24f94fde..587e2b8b8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -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");