mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Scarf joint seam enhancement: conditional scarf joint and slowdown for scarf joint only (#4317)
* Allow apply scarf joint seams to perimeters without sharp corners only * 1. Fix an error when detect whether a loop is smooth 2. Expose scarf_angle_threshold to UI * fix linux build error * minor code changes * Support slowdown speed for scarf joint only * update tips * improve the logic a bit * Fixed a bug that scarf speed may not respected for overhangs * Add a new scarf flow ratio option
This commit is contained in:
		
							parent
							
								
									6264fe64b4
								
							
						
					
					
						commit
						a4bf3dabb4
					
				
					 10 changed files with 144 additions and 4 deletions
				
			
		|  | @ -7,6 +7,7 @@ | |||
| #include <cmath> | ||||
| #include <limits> | ||||
| #include <sstream> | ||||
| #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<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, | ||||
|  |  | |||
|  | @ -479,7 +479,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 | ||||
|  |  | |||
|  | @ -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()) { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -820,7 +820,7 @@ static std::vector<std::string> s_Preset_print_options { | |||
|      "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", | ||||
|      "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<std::string> s_Preset_filament_options { | ||||
|  |  | |||
|  | @ -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>(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" | ||||
|  |  | |||
|  | @ -953,11 +953,17 @@ PRINT_CONFIG_CLASS_DEFINE( | |||
| 
 | ||||
|     // 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)) | ||||
|     ((ConfigOptionFloatOrPercent,       scarf_joint_speed)) | ||||
|     ((ConfigOptionFloat,                scarf_joint_flow_ratio)) | ||||
| 
 | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| PRINT_CONFIG_CLASS_DEFINE( | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
|  | @ -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<SeamScarfType>("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*/) | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 SoftFever
						SoftFever