diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 6b75c0ee98..dee7477939 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "Utils.hpp" #define L(s) (s) @@ -340,6 +341,65 @@ 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, diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index cb350386e9..2ff7d5fb0b 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -479,7 +479,8 @@ class ExtrusionLoop : public ExtrusionEntity 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 diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8253551b7f..3b68d6ecf3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4557,11 +4557,14 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou loop.split_at(last_pos, false); const auto seam_scarf_type = m_config.seam_slope_type.value; - const bool enable_seam_slope = ((seam_scarf_type == SeamScarfType::External && !is_hole) || seam_scarf_type == SeamScarfType::All) && + bool enable_seam_slope = ((seam_scarf_type == SeamScarfType::External && !is_hole) || seam_scarf_type == SeamScarfType::All) && !m_config.spiral_mode && (loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) && layer_id() > 0; + 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 @@ -5068,6 +5071,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, _mm3_per_mm *= m_config.bottom_solid_infill_flow_ratio; else if (path.role() == erInternalBridgeInfill) _mm3_per_mm *= m_config.internal_bridge_flow; + else if(sloped) + _mm3_per_mm *= m_config.scarf_joint_flow_ratio; double e_per_mm = m_writer.extruder()->e_per_mm3() * _mm3_per_mm; @@ -5082,6 +5087,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double new_speed = m_config.get_abs_value(overhang_speed_key_map[overhang_degree].c_str()); speed = new_speed == 0.0 ? speed : new_speed; } + + if (sloped) { + speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(m_config.get_abs_value("inner_wall_speed"))); + } } else if (path.role() == erExternalPerimeter) { speed = m_config.get_abs_value("outer_wall_speed"); if (m_config.overhang_speed_classic.value && m_config.enable_overhang_speed.value && @@ -5089,6 +5098,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double new_speed = m_config.get_abs_value(overhang_speed_key_map[overhang_degree].c_str()); speed = new_speed == 0.0 ? speed : new_speed; } + if (sloped) { + speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(m_config.get_abs_value("outer_wall_speed"))); + } } else if(path.role() == erInternalBridgeInfill) { speed = m_config.get_abs_value("internal_bridge_speed"); @@ -5172,6 +5184,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, (is_bridge(path.role()) || is_perimeter(path.role()))) { bool is_external = is_external_perimeter(path.role()); double ref_speed = is_external ? m_config.get_abs_value("outer_wall_speed") : m_config.get_abs_value("inner_wall_speed"); + if (sloped) { + ref_speed = std::min(ref_speed, m_config.scarf_joint_speed.get_abs_value(ref_speed)); + } ConfigOptionPercents overhang_overlap_levels({75, 50, 25, 13, 12.99, 0}); if (m_config.slowdown_for_curled_perimeters){ @@ -5235,6 +5250,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } double F = speed * 60; // convert mm/sec to mm/min + if(abs(F - 5753.504) < 0.002) + { + std::cout << "F: " << F << std::endl; + } //Orca: process custom gcode for extrusion role change if (path.role() != m_last_extrusion_role && !m_config.change_extrusion_role_gcode.value.empty()) { diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index edea7411ef..6487ddb40a 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -186,6 +186,10 @@ void Layer::make_perimeters() && config.fuzzy_skin_point_distance == other_config.fuzzy_skin_point_distance && config.fuzzy_skin_first_layer == other_config.fuzzy_skin_first_layer && 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.scarf_joint_speed == other_config.scarf_joint_speed + && config.scarf_joint_flow_ratio == other_config.scarf_joint_flow_ratio && 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 diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f9ed88d4f1..b99af03736 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -820,7 +820,7 @@ static std::vector s_Preset_print_options { "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", - "seam_slope_type", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls", + "seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "scarf_joint_speed", "scarf_joint_flow_ratio", "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 { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 984192bd9f..63a7a96546 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3547,7 +3547,45 @@ def = this->add("filament_loading_speed", coFloats); def->enum_labels.push_back(L("Contour and hole")); def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(SeamScarfType::None)); - + + 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(false)); + + 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("scarf_joint_speed", coFloatOrPercent); + def->label = L("Scarf joint speed"); + def->category = L("Quality"); + def->tooltip = L( + "This option sets the printing speed for scarf joints. It is recommended to print scarf joints at a slow speed (less than 100 " + "mm/s). It's also advisable to enable 'Extrusion rate smoothing' if the set speed varies significantly from the speed of the " + "outer or inner walls. If the speed specified here is higher than the speed of the outer or inner walls, the printer will default " + "to the slower of the two speeds. When specified as a percentage (e.g., 80%), the speed is calculated based on the respective " + "outer or inner wall speed. The default value is set to 100%."); + def->sidetext = L("mm/s or %"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("scarf_joint_flow_ratio", coFloat); + def->label = L("Scarf joint flow ratio"); + def->tooltip = L("This factor affects the amount of material for scarf joints."); + def->mode = comAdvanced; + def->max = 2; + def->set_default_value(new ConfigOptionFloat(1)); + def = this->add("seam_slope_start_height", coFloatOrPercent); def->label = L("Scarf start height"); def->tooltip = L("Start height of the scarf.\n" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 07be77ab3d..3129fd3589 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -953,11 +953,17 @@ PRINT_CONFIG_CLASS_DEFINE( // 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)) + ((ConfigOptionFloatOrPercent, scarf_joint_speed)) + ((ConfigOptionFloat, scarf_joint_flow_ratio)) + + ) PRINT_CONFIG_CLASS_DEFINE( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2233057ece..3d9672025a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1143,6 +1143,10 @@ bool PrintObject::invalidate_state_by_config_options( } else if ( opt_key == "seam_position" || opt_key == "seam_slope_type" + || opt_key == "seam_slope_conditional" + || opt_key == "scarf_angle_threshold" + || opt_key == "scarf_joint_speed" + || opt_key == "scarf_joint_flow_ratio" || opt_key == "seam_slope_start_height" || opt_key == "seam_slope_entire_loop" || opt_key == "seam_slope_min_length" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 1c0667b298..79ee44e846 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -756,12 +756,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co 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("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_line("scarf_joint_speed", has_seam_slope); + toggle_line("scarf_joint_flow_ratio", has_seam_slope); toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop")); + toggle_line("scarf_angle_threshold", has_seam_slope && config->opt_bool("seam_slope_conditional")); } 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 d4213df488..b3c8297adc 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1977,10 +1977,14 @@ void TabPrint::build() optgroup->append_single_option_line("staggered_inner_seams", "seam"); optgroup->append_single_option_line("seam_gap","seam"); optgroup->append_single_option_line("seam_slope_type", "seam#scarf-joint-seam"); + optgroup->append_single_option_line("seam_slope_conditional", "seam#scarf-joint-seam"); + optgroup->append_single_option_line("scarf_angle_threshold", "seam#scarf-joint-seam"); + optgroup->append_single_option_line("scarf_joint_speed", "seam#scarf-joint-seam"); optgroup->append_single_option_line("seam_slope_start_height", "seam#scarf-joint-seam"); optgroup->append_single_option_line("seam_slope_entire_loop", "seam#scarf-joint-seam"); optgroup->append_single_option_line("seam_slope_min_length", "seam#scarf-joint-seam"); optgroup->append_single_option_line("seam_slope_steps", "seam#scarf-joint-seam"); + optgroup->append_single_option_line("scarf_joint_flow_ratio", "seam#scarf-joint-seam"); optgroup->append_single_option_line("seam_slope_inner_walls", "seam#scarf-joint-seam"); optgroup->append_single_option_line("role_based_wipe_speed","seam"); optgroup->append_single_option_line("wipe_speed", "seam");