mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 20:51:12 -06:00 
			
		
		
		
	Seam: use scarf joint to minimize seam visiblity (#3839)
* Remember z of previous layer
* Support travel to middle of the layer z
* Support sloped extrusion
* Implement sloped seam
* Reduce extra movements
* Don't clip loop if sloped seam is enabled
* Fix wipe
* Ensure `slope_max_segment_length`
* Add options
* Limit slope length to perimeter length
* Fix slope segmentation
* Rename the option to scarf joint seam
* Don't modify the slope option when turning on spiral vase
* Add a few suggestions when turnning on scarf joint
* Add option to add scarf joint to inner walls
* Apply seam gap at the end of the slope
* Add option to explicitly use the entire loop as scarf length
* Fix layer number
* Increase default scarf length to 20mm
* Better way of storing the global scarf state
* Better vase mode layer height recognition
* Move id should exclude seams
* Fix slope height with independent support layer height
* Fix linux build
* Allow controlling the scarf with modifier
* Scarf start height default to 0
* Allow enable scarf seam on contour only
* Fix type error
* Move the creation of sloped loop into ExtrusionEntity.cpp
* Fix error "vector too long"
* Detect seams properly
* The correct way of calculating the rate limit
* The correct way of calculating the rate limit
(cherry picked from commit 05961f7c98)
* Add pressure equalizer in print by object mode
* Remove the settings recommendation as it varies a lot depends on printer & filament
* Add a beta suffix
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									ab1b0e0ebc
								
							
						
					
					
						commit
						924a2b4551
					
				
					 16 changed files with 533 additions and 54 deletions
				
			
		|  | @ -340,6 +340,115 @@ double ExtrusionLoop::min_mm3_per_mm() const | ||||||
|     return min_mm3_per_mm; |     return min_mm3_per_mm; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths&   original_paths, | ||||||
|  |                                          double            seam_gap, | ||||||
|  |                                          double            slope_min_length, | ||||||
|  |                                          double            slope_max_segment_length, | ||||||
|  |                                          double            start_slope_ratio, | ||||||
|  |                                          ExtrusionLoopRole role) | ||||||
|  |     : ExtrusionLoop(role) | ||||||
|  | { | ||||||
|  |     // create slopes
 | ||||||
|  |     const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath& path, const Polyline& poly, | ||||||
|  |                                                                           double ratio_begin, double ratio_end) { | ||||||
|  |         if (poly.empty()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Ensure `slope_max_segment_length`
 | ||||||
|  |         Polyline detailed_poly; | ||||||
|  |         { | ||||||
|  |             detailed_poly.append(poly.first_point()); | ||||||
|  | 
 | ||||||
|  |             // Recursively split the line into half until no longer than `slope_max_segment_length`
 | ||||||
|  |             const std::function<void(const Line&)> handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line& line) { | ||||||
|  |                 if (line.length() <= slope_max_segment_length) { | ||||||
|  |                     detailed_poly.append(line.b); | ||||||
|  |                 } else { | ||||||
|  |                     // Then process left half
 | ||||||
|  |                     handle_line({line.a, line.midpoint()}); | ||||||
|  |                     // Then process right half
 | ||||||
|  |                     handle_line({line.midpoint(), line.b}); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             for (const auto& l : poly.lines()) { | ||||||
|  |                 handle_line(l); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         starts.emplace_back(detailed_poly, path, ExtrusionPathSloped::Slope{ratio_begin, ratio_begin}, | ||||||
|  |                                     ExtrusionPathSloped::Slope{ratio_end, ratio_end}); | ||||||
|  | 
 | ||||||
|  |         if (is_approx(ratio_end, 1.) && seam_gap > 0) { | ||||||
|  |             // Remove the segments that has no extrusion
 | ||||||
|  |             const auto seg_length = detailed_poly.length(); | ||||||
|  |             if (seg_length > seam_gap) { | ||||||
|  |                 // Split the segment and remove the last `seam_gap` bit
 | ||||||
|  |                 const Polyline orig = detailed_poly; | ||||||
|  |                 Polyline       tmp; | ||||||
|  |                 orig.split_at_length(seg_length - seam_gap, &detailed_poly, &tmp); | ||||||
|  | 
 | ||||||
|  |                 ratio_end = lerp(ratio_begin, ratio_end, (seg_length - seam_gap) / seg_length); | ||||||
|  |                 assert(1. - ratio_end > EPSILON); | ||||||
|  |             } else { | ||||||
|  |                 // Remove the entire segment
 | ||||||
|  |                 detailed_poly.clear(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!detailed_poly.empty()) { | ||||||
|  |             ends.emplace_back(detailed_poly, path, ExtrusionPathSloped::Slope{1., 1. - ratio_begin}, | ||||||
|  |                                       ExtrusionPathSloped::Slope{1., 1. - ratio_end}); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     double remaining_length = slope_min_length; | ||||||
|  | 
 | ||||||
|  |     ExtrusionPaths::iterator path        = original_paths.begin(); | ||||||
|  |     double                   start_ratio = start_slope_ratio; | ||||||
|  |     for (; path != original_paths.end() && remaining_length > 0; ++path) { | ||||||
|  |         const double path_len = unscale_(path->length()); | ||||||
|  |         if (path_len > remaining_length) { | ||||||
|  |             // Split current path into slope and non-slope part
 | ||||||
|  |             Polyline slope_path; | ||||||
|  |             Polyline flat_path; | ||||||
|  |             path->polyline.split_at_length(scale_(remaining_length), &slope_path, &flat_path); | ||||||
|  | 
 | ||||||
|  |             add_slop(*path, slope_path, start_ratio, 1); | ||||||
|  |             start_ratio = 1; | ||||||
|  | 
 | ||||||
|  |             paths.emplace_back(std::move(flat_path), *path); | ||||||
|  |             remaining_length = 0; | ||||||
|  |         } else { | ||||||
|  |             remaining_length -= path_len; | ||||||
|  |             const double end_ratio = lerp(1.0, start_slope_ratio, remaining_length / slope_min_length); | ||||||
|  |             add_slop(*path, path->polyline, start_ratio, end_ratio); | ||||||
|  |             start_ratio = end_ratio; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     assert(remaining_length <= 0); | ||||||
|  |     assert(start_ratio == 1.); | ||||||
|  | 
 | ||||||
|  |     // Put remaining flat paths
 | ||||||
|  |     paths.insert(paths.end(), path, original_paths.end()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<const ExtrusionPath*> ExtrusionLoopSloped::get_all_paths() const { | ||||||
|  |     std::vector<const ExtrusionPath*> r; | ||||||
|  |     r.reserve(starts.size() + paths.size() + ends.size()); | ||||||
|  |     for (const auto& p : starts) { | ||||||
|  |         r.push_back(&p); | ||||||
|  |     } | ||||||
|  |     for (const auto& p : paths) { | ||||||
|  |         r.push_back(&p); | ||||||
|  |     } | ||||||
|  |     for (const auto& p : ends) { | ||||||
|  |         r.push_back(&p); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return r; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| std::string ExtrusionEntity::role_to_string(ExtrusionRole role) | std::string ExtrusionEntity::role_to_string(ExtrusionRole role) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -301,6 +301,42 @@ private: | ||||||
|     bool m_no_extrusion = false; |     bool m_no_extrusion = false; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class ExtrusionPathSloped : public ExtrusionPath | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     struct Slope | ||||||
|  |     { | ||||||
|  |         double z_ratio{1.}; | ||||||
|  |         double e_ratio{1.}; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Slope slope_begin; | ||||||
|  |     Slope slope_end; | ||||||
|  | 
 | ||||||
|  |     ExtrusionPathSloped(const ExtrusionPath& rhs, const Slope& begin, const Slope& end) | ||||||
|  |         : ExtrusionPath(rhs), slope_begin(begin), slope_end(end) | ||||||
|  |     {} | ||||||
|  |     ExtrusionPathSloped(ExtrusionPath&& rhs, const Slope& begin, const Slope& end) | ||||||
|  |         : ExtrusionPath(std::move(rhs)), slope_begin(begin), slope_end(end) | ||||||
|  |     {} | ||||||
|  |     ExtrusionPathSloped(const Polyline& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end) | ||||||
|  |         : ExtrusionPath(polyline, rhs), slope_begin(begin), slope_end(end) | ||||||
|  |     {} | ||||||
|  |     ExtrusionPathSloped(Polyline&& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end) | ||||||
|  |         : ExtrusionPath(std::move(polyline), rhs), slope_begin(begin), slope_end(end) | ||||||
|  |     {} | ||||||
|  | 
 | ||||||
|  |     Slope interpolate(const double ratio) const | ||||||
|  |     { | ||||||
|  |         return { | ||||||
|  |             lerp(slope_begin.z_ratio, slope_end.z_ratio, ratio), | ||||||
|  |             lerp(slope_begin.e_ratio, slope_end.e_ratio, ratio), | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool is_flat() const { return is_approx(slope_begin.z_ratio, slope_end.z_ratio); } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class ExtrusionPathOriented : public ExtrusionPath | class ExtrusionPathOriented : public ExtrusionPath | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  | @ -459,6 +495,22 @@ private: | ||||||
|     ExtrusionLoopRole m_loop_role; |     ExtrusionLoopRole m_loop_role; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class ExtrusionLoopSloped : public ExtrusionLoop | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     std::vector<ExtrusionPathSloped> starts; | ||||||
|  |     std::vector<ExtrusionPathSloped> ends; | ||||||
|  | 
 | ||||||
|  |     ExtrusionLoopSloped(ExtrusionPaths& original_paths, | ||||||
|  |                         double          seam_gap, | ||||||
|  |                         double          slope_min_length, | ||||||
|  |                         double          slope_max_segment_length, | ||||||
|  |                         double          start_slope_ratio, | ||||||
|  |                         ExtrusionLoopRole role = elrDefault); | ||||||
|  | 
 | ||||||
|  |     [[nodiscard]] std::vector<const ExtrusionPath*> get_all_paths() const; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) | inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) | ||||||
| { | { | ||||||
|     dst.reserve(dst.size() + polylines.size()); |     dst.reserve(dst.size() + polylines.size()); | ||||||
|  |  | ||||||
|  | @ -4534,7 +4534,6 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la | ||||||
|     return out; |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters) | std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters) | ||||||
| { | { | ||||||
|     // get a copy; don't modify the orientation of the original loop object otherwise
 |     // get a copy; don't modify the orientation of the original loop object otherwise
 | ||||||
|  | @ -4557,11 +4556,17 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou | ||||||
|     } else |     } else | ||||||
|         loop.split_at(last_pos, false); |         loop.split_at(last_pos, false); | ||||||
| 
 | 
 | ||||||
|  |     const auto seam_scarf_type = m_config.seam_slope_type.value; | ||||||
|  |     const bool enable_seam_slope = ((seam_scarf_type == SeamScarfType::External && !is_hole) || seam_scarf_type == SeamScarfType::All) && | ||||||
|  |         !m_config.spiral_mode && | ||||||
|  |         (loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) && | ||||||
|  |         layer_id() > 0; | ||||||
|  | 
 | ||||||
|     // clip the path to avoid the extruder to get exactly on the first point of the loop;
 |     // clip the path to avoid the extruder to get exactly on the first point of the loop;
 | ||||||
|     // if polyline was shorter than the clipping distance we'd get a null polyline, so
 |     // if polyline was shorter than the clipping distance we'd get a null polyline, so
 | ||||||
|     // we discard it in that case
 |     // we discard it in that case
 | ||||||
|     double clip_length = m_enable_loop_clipping ? |     const double seam_gap = scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter))); | ||||||
|     scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter))) : 0; |     const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0; | ||||||
| 
 | 
 | ||||||
|     // get paths
 |     // get paths
 | ||||||
|     ExtrusionPaths paths; |     ExtrusionPaths paths; | ||||||
|  | @ -4650,15 +4655,54 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|      | 
 | ||||||
|      |     const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath& path) { | ||||||
|     bool is_small_peri = false; |  | ||||||
|     for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { |  | ||||||
| //    description += ExtrusionLoop::role_to_string(loop.loop_role());
 |  | ||||||
| //    description += ExtrusionEntity::role_to_string(path->role);
 |  | ||||||
|         // don't apply small perimeter setting for overhangs/bridges/non-perimeters
 |         // don't apply small perimeter setting for overhangs/bridges/non-perimeters
 | ||||||
|         is_small_peri = is_perimeter(path->role()) && !is_bridge(path->role()) && small_peri_speed > 0 && (path->get_overhang_degree() == 0 || path->get_overhang_degree() > 5); |         const bool is_small_peri = is_perimeter(path.role()) && !is_bridge(path.role()) && small_peri_speed > 0 && (path.get_overhang_degree() == 0 || path.get_overhang_degree() > 5); | ||||||
|         gcode += this->_extrude(*path, description, is_small_peri ? small_peri_speed : speed); |         return is_small_peri ? small_peri_speed : speed; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (!enable_seam_slope) { | ||||||
|  |         for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { | ||||||
|  |             gcode += this->_extrude(*path, description, speed_for_path(*path)); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         // Create seam slope
 | ||||||
|  |         double start_slope_ratio; | ||||||
|  |         if (m_config.seam_slope_start_height.percent) { | ||||||
|  |             start_slope_ratio = m_config.seam_slope_start_height.value / 100.; | ||||||
|  |         } else { | ||||||
|  |             // Get the ratio against current layer height
 | ||||||
|  |             double h = paths.front().height; | ||||||
|  |             start_slope_ratio = m_config.seam_slope_start_height.value / h; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         double loop_length = 0.; | ||||||
|  |         for (const auto & path : paths) { | ||||||
|  |             loop_length += unscale_(path.length()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const bool   slope_entire_loop        = m_config.seam_slope_entire_loop; | ||||||
|  |         const double slope_min_length         = slope_entire_loop ? loop_length : std::min(m_config.seam_slope_min_length.value, loop_length); | ||||||
|  |         const int    slope_steps              = m_config.seam_slope_steps; | ||||||
|  |         const double slope_max_segment_length = scale_(slope_min_length / slope_steps); | ||||||
|  | 
 | ||||||
|  |         // Calculate the sloped loop
 | ||||||
|  |         ExtrusionLoopSloped new_loop(paths, seam_gap, slope_min_length, slope_max_segment_length, start_slope_ratio, loop.loop_role()); | ||||||
|  | 
 | ||||||
|  |         // Then extrude it
 | ||||||
|  |         for (const auto& p : new_loop.get_all_paths()) { | ||||||
|  |             gcode += this->_extrude(*p, description, speed_for_path(*p)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Fix path for wipe
 | ||||||
|  |         if (!new_loop.ends.empty()) { | ||||||
|  |             paths.clear(); | ||||||
|  |             // The start slope part is ignored as it overlaps with the end part
 | ||||||
|  |             paths.reserve(new_loop.paths.size() + new_loop.ends.size()); | ||||||
|  |             paths.insert(paths.end(), new_loop.paths.begin(), new_loop.paths.end()); | ||||||
|  |             paths.insert(paths.end(), new_loop.ends.begin(), new_loop.ends.end()); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // BBS
 |     // BBS
 | ||||||
|  | @ -4932,14 +4976,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|     if (is_bridge(path.role())) |     if (is_bridge(path.role())) | ||||||
|         description += " (bridge)"; |         description += " (bridge)"; | ||||||
| 
 | 
 | ||||||
|  |     const ExtrusionPathSloped* sloped = dynamic_cast<const ExtrusionPathSloped*>(&path); | ||||||
|  | 
 | ||||||
|  |     const auto get_sloped_z = [&sloped, this](double z_ratio) { | ||||||
|  |         const auto height = sloped->height; | ||||||
|  |         return lerp(m_nominal_z - height, m_nominal_z, z_ratio); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     // go to first point of extrusion path
 |     // go to first point of extrusion path
 | ||||||
|     //BBS: path.first_point is 2D point. But in lazy raise case, lift z is done in travel_to function.
 |     //BBS: path.first_point is 2D point. But in lazy raise case, lift z is done in travel_to function.
 | ||||||
|     //Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance
 |     //Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance
 | ||||||
|     if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z) { |     if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z || (sloped != nullptr && !sloped->is_flat())) { | ||||||
|         gcode += this->travel_to( |         gcode += this->travel_to( | ||||||
|             path.first_point(), |             path.first_point(), | ||||||
|             path.role(), |             path.role(), | ||||||
|             "move to first " + description + " point" |             "move to first " + description + " point", | ||||||
|  |             sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio) | ||||||
|         ); |         ); | ||||||
|         m_need_change_layer_lift_z = false; |         m_need_change_layer_lift_z = false; | ||||||
|     } |     } | ||||||
|  | @ -5290,7 +5342,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|     if (!variable_speed) { |     if (!variable_speed) { | ||||||
|         // F is mm per minute.
 |         // F is mm per minute.
 | ||||||
|         gcode += m_writer.set_speed(F, "", comment); |         gcode += m_writer.set_speed(F, "", comment); | ||||||
|         double path_length = 0.; |  | ||||||
|         { |         { | ||||||
|             if (m_enable_cooling_markers) { |             if (m_enable_cooling_markers) { | ||||||
|                 if (enable_overhang_bridge_fan) { |                 if (enable_overhang_bridge_fan) { | ||||||
|  | @ -5323,9 +5374,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             // BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode
 |             // BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode or we are doing sloped extrusion
 | ||||||
|             // Attention: G2 and G3 is not supported in spiral_mode mode
 |             // Attention: G2 and G3 is not supported in spiral_mode mode
 | ||||||
|             if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode) { |             if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr) { | ||||||
|  |                 double path_length = 0.; | ||||||
|  |                 double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR; | ||||||
|                 for (const Line& line : path.polyline.lines()) { |                 for (const Line& line : path.polyline.lines()) { | ||||||
|                     std::string tempDescription = description; |                     std::string tempDescription = description; | ||||||
|                     const double line_length = line.length() * SCALING_FACTOR; |                     const double line_length = line.length() * SCALING_FACTOR; | ||||||
|  | @ -5339,10 +5392,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|                             tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length); |                             tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     gcode += m_writer.extrude_to_xy( |                     if (sloped == nullptr) { | ||||||
|                         this->point_to_gcode(line.b), |                         // Normal extrusion
 | ||||||
|                         dE, |                         gcode += m_writer.extrude_to_xy( | ||||||
|                         GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); |                             this->point_to_gcode(line.b), | ||||||
|  |                             dE, | ||||||
|  |                             GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); | ||||||
|  |                     } else { | ||||||
|  |                         // Sloped extrusion
 | ||||||
|  |                         const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length); | ||||||
|  |                         Vec2d dest2d = this->point_to_gcode(line.b); | ||||||
|  |                         Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio)); | ||||||
|  |                         gcode += m_writer.extrude_to_xyz( | ||||||
|  |                             dest3d, | ||||||
|  |                             dE * e_ratio, | ||||||
|  |                             GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 // BBS: start to generate gcode from arc fitting data which includes line and arc
 |                 // BBS: start to generate gcode from arc fitting data which includes line and arc
 | ||||||
|  | @ -5356,7 +5421,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|                         for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) { |                         for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) { | ||||||
|                             const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]); |                             const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]); | ||||||
|                             const double line_length = line.length() * SCALING_FACTOR; |                             const double line_length = line.length() * SCALING_FACTOR; | ||||||
|                             path_length += line_length; |  | ||||||
|                             auto dE = e_per_mm * line_length; |                             auto dE = e_per_mm * line_length; | ||||||
|                             if (m_small_area_infill_flow_compensator  && m_config.small_area_infill_flow_compensation.value) { |                             if (m_small_area_infill_flow_compensator  && m_config.small_area_infill_flow_compensation.value) { | ||||||
|                                 auto oldE = dE; |                                 auto oldE = dE; | ||||||
|  | @ -5378,7 +5442,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|                         const ArcSegment& arc = fitting_result[fitting_index].arc_data; |                         const ArcSegment& arc = fitting_result[fitting_index].arc_data; | ||||||
|                         const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR; |                         const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR; | ||||||
|                         const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point); |                         const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point); | ||||||
|                         path_length += arc_length; |  | ||||||
|                         auto dE = e_per_mm * arc_length; |                         auto dE = e_per_mm * arc_length; | ||||||
|                         if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) { |                         if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) { | ||||||
|                             auto oldE = dE; |                             auto oldE = dE; | ||||||
|  | @ -5407,6 +5470,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|     } else { |     } else { | ||||||
|         double last_set_speed = new_points[0].speed * 60.0; |         double last_set_speed = new_points[0].speed * 60.0; | ||||||
| 
 | 
 | ||||||
|  |         double total_length = 0; | ||||||
|  |         if (sloped != nullptr) { | ||||||
|  |             // Calculate total extrusion length
 | ||||||
|  |             Points p; | ||||||
|  |             p.reserve(new_points.size()); | ||||||
|  |             std::transform(new_points.begin(), new_points.end(), std::back_inserter(p), [](const ProcessedPoint& pp) { return pp.p; }); | ||||||
|  |             Polyline l(p); | ||||||
|  |             total_length = l.length() * SCALING_FACTOR; | ||||||
|  |         } | ||||||
|         gcode += m_writer.set_speed(last_set_speed, "", comment); |         gcode += m_writer.set_speed(last_set_speed, "", comment); | ||||||
|         Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); |         Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); | ||||||
|         bool pre_fan_enabled = false; |         bool pre_fan_enabled = false; | ||||||
|  | @ -5414,6 +5486,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|         if( m_enable_cooling_markers && enable_overhang_bridge_fan) |         if( m_enable_cooling_markers && enable_overhang_bridge_fan) | ||||||
|             pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role()); |             pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role()); | ||||||
| 
 | 
 | ||||||
|  |         double path_length = 0.; | ||||||
|         for (size_t i = 1; i < new_points.size(); i++) { |         for (size_t i = 1; i < new_points.size(); i++) { | ||||||
|             std::string tempDescription = description; |             std::string tempDescription = description; | ||||||
|             const ProcessedPoint &processed_point = new_points[i]; |             const ProcessedPoint &processed_point = new_points[i]; | ||||||
|  | @ -5449,6 +5522,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const double line_length = (p - prev).norm(); |             const double line_length = (p - prev).norm(); | ||||||
|  |             path_length += line_length; | ||||||
|             double new_speed = pre_processed_point.speed * 60.0; |             double new_speed = pre_processed_point.speed * 60.0; | ||||||
|             if (last_set_speed != new_speed) { |             if (last_set_speed != new_speed) { | ||||||
|                 gcode += m_writer.set_speed(new_speed, "", comment); |                 gcode += m_writer.set_speed(new_speed, "", comment); | ||||||
|  | @ -5463,8 +5537,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, | ||||||
|                     tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length); |                     tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             gcode += |             if (sloped == nullptr) { | ||||||
|                 m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : ""); |                 // Normal extrusion
 | ||||||
|  |                 gcode += m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : ""); | ||||||
|  |             } else { | ||||||
|  |                 // Sloped extrusion
 | ||||||
|  |                 const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length); | ||||||
|  |                 Vec3d dest3d(p(0), p(1), get_sloped_z(z_ratio)); | ||||||
|  |                 gcode += m_writer.extrude_to_xyz(dest3d, dE * e_ratio, GCodeWriter::full_gcode_comment ? tempDescription : ""); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             prev = p; |             prev = p; | ||||||
| 
 | 
 | ||||||
|  | @ -5543,7 +5624,7 @@ std::string GCode::_encode_label_ids_to_base64(std::vector<size_t> ids) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // This method accepts &point in print coordinates.
 | // This method accepts &point in print coordinates.
 | ||||||
| std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) | std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string comment, double z/* = DBL_MAX*/) | ||||||
| { | { | ||||||
|     /*  Define the travel move as a line between current position and the taget point.
 |     /*  Define the travel move as a line between current position and the taget point.
 | ||||||
|         This is expressed in print coordinates, so it will need to be translated by |         This is expressed in print coordinates, so it will need to be translated by | ||||||
|  | @ -5628,15 +5709,36 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string | ||||||
| 
 | 
 | ||||||
|     // use G1 because we rely on paths being straight (G0 may make round paths)
 |     // use G1 because we rely on paths being straight (G0 may make round paths)
 | ||||||
|     if (travel.size() >= 2) { |     if (travel.size() >= 2) { | ||||||
|         for (size_t i = 1; i < travel.size(); ++ i) { |         if (m_spiral_vase) { | ||||||
|             // BBS. Process lazy layer change, but don't do lazy layer change when enable spiral vase
 |             // No lazy z lift for spiral vase mode
 | ||||||
|             Vec3d curr_pos = m_writer.get_position(); |             for (size_t i = 1; i < travel.size(); ++i) { | ||||||
|             if (i == 1 && !m_spiral_vase) { |                 gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy"); | ||||||
|                 Vec2d dest2d = this->point_to_gcode(travel.points[i]); |             } | ||||||
|                 Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z); |         } else { | ||||||
|                 gcode += m_writer.travel_to_xyz(dest3d, comment+" travel_to_xyz"); |             if (travel.size() == 2) { | ||||||
|  |                 // No extra movements emitted by avoid_crossing_perimeters, simply move to the end point with z change
 | ||||||
|  |                 const auto& dest2d = this->point_to_gcode(travel.points.back()); | ||||||
|  |                 Vec3d dest3d(dest2d(0), dest2d(1), z == DBL_MAX ? m_nominal_z : z); | ||||||
|  |                 gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz"); | ||||||
|             } else { |             } else { | ||||||
|                 gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment+" travel_to_xy"); |                 // Extra movements emitted by avoid_crossing_perimeters, lift the z to normal height at the beginning, then apply the z
 | ||||||
|  |                 // ratio at the last point
 | ||||||
|  |                 for (size_t i = 1; i < travel.size(); ++i) { | ||||||
|  |                     if (i == 1) { | ||||||
|  |                         // Lift to normal z at beginning
 | ||||||
|  |                         Vec2d dest2d = this->point_to_gcode(travel.points[i]); | ||||||
|  |                         Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z); | ||||||
|  |                         gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz"); | ||||||
|  |                     } else if (z != DBL_MAX && i == travel.size() - 1) { | ||||||
|  |                         // Apply z_ratio for the very last point
 | ||||||
|  |                         Vec2d dest2d = this->point_to_gcode(travel.points[i]); | ||||||
|  |                         Vec3d dest3d(dest2d(0), dest2d(1), z); | ||||||
|  |                         gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz"); | ||||||
|  |                     } else { | ||||||
|  |                         // For all points in between, no z change
 | ||||||
|  |                         gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy"); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         this->set_last_pos(travel.points.back()); |         this->set_last_pos(travel.points.back()); | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
| #include <map> | #include <map> | ||||||
| #include <set> | #include <set> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <cfloat> | ||||||
| 
 | 
 | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|  | @ -219,7 +220,7 @@ public: | ||||||
|     void            set_layer_count(unsigned int value) { m_layer_count = value; } |     void            set_layer_count(unsigned int value) { m_layer_count = value; } | ||||||
|     void            apply_print_config(const PrintConfig &print_config); |     void            apply_print_config(const PrintConfig &print_config); | ||||||
| 
 | 
 | ||||||
|     std::string     travel_to(const Point& point, ExtrusionRole role, std::string comment); |     std::string     travel_to(const Point& point, ExtrusionRole role, std::string comment, double z = DBL_MAX); | ||||||
|     bool            needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type); |     bool            needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type); | ||||||
|     std::string     retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift); |     std::string     retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift); | ||||||
|     std::string     unretract() { return m_writer.unlift() + m_writer.unretract(); } |     std::string     unretract() { return m_writer.unlift() + m_writer.unretract(); } | ||||||
|  |  | ||||||
|  | @ -1080,7 +1080,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config) | ||||||
| 
 | 
 | ||||||
|     const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode"); |     const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode"); | ||||||
|     if (spiral_vase != nullptr) |     if (spiral_vase != nullptr) | ||||||
|         m_spiral_vase_active = spiral_vase->value; |         m_detect_layer_based_on_tag = spiral_vase->value; | ||||||
|  | 
 | ||||||
|  |     const ConfigOptionBool* has_scarf_joint_seam = config.option<ConfigOptionBool>("has_scarf_joint_seam"); | ||||||
|  |     if (has_scarf_joint_seam != nullptr) | ||||||
|  |         m_detect_layer_based_on_tag = m_detect_layer_based_on_tag || has_scarf_joint_seam->value; | ||||||
| 
 | 
 | ||||||
|     const ConfigOptionBool* manual_filament_change = config.option<ConfigOptionBool>("manual_filament_change"); |     const ConfigOptionBool* manual_filament_change = config.option<ConfigOptionBool>("manual_filament_change"); | ||||||
|     if (manual_filament_change != nullptr) |     if (manual_filament_change != nullptr) | ||||||
|  | @ -1397,7 +1401,11 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) | ||||||
| 
 | 
 | ||||||
|     const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode"); |     const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode"); | ||||||
|     if (spiral_vase != nullptr) |     if (spiral_vase != nullptr) | ||||||
|         m_spiral_vase_active = spiral_vase->value; |         m_detect_layer_based_on_tag = spiral_vase->value; | ||||||
|  | 
 | ||||||
|  |     const ConfigOptionBool* has_scarf_joint_seam = config.option<ConfigOptionBool>("has_scarf_joint_seam"); | ||||||
|  |     if (has_scarf_joint_seam != nullptr) | ||||||
|  |         m_detect_layer_based_on_tag = m_detect_layer_based_on_tag || has_scarf_joint_seam->value; | ||||||
| 
 | 
 | ||||||
|     const ConfigOptionEnumGeneric *bed_type = config.option<ConfigOptionEnumGeneric>("curr_bed_type"); |     const ConfigOptionEnumGeneric *bed_type = config.option<ConfigOptionEnumGeneric>("curr_bed_type"); | ||||||
|     if (bed_type != nullptr) |     if (bed_type != nullptr) | ||||||
|  | @ -1479,7 +1487,9 @@ void GCodeProcessor::reset() | ||||||
| 
 | 
 | ||||||
|     m_options_z_corrector.reset(); |     m_options_z_corrector.reset(); | ||||||
| 
 | 
 | ||||||
|     m_spiral_vase_active = false; |     m_detect_layer_based_on_tag = false; | ||||||
|  | 
 | ||||||
|  |     m_seams_count = 0; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_GCODE_VIEWER_DATA_CHECKING | #if ENABLE_GCODE_VIEWER_DATA_CHECKING | ||||||
|     m_mm3_per_mm_compare.reset(); |     m_mm3_per_mm_compare.reset(); | ||||||
|  | @ -2344,12 +2354,12 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers | ||||||
|     // layer change tag
 |     // layer change tag
 | ||||||
|     if (comment == reserved_tag(ETags::Layer_Change)) { |     if (comment == reserved_tag(ETags::Layer_Change)) { | ||||||
|         ++m_layer_id; |         ++m_layer_id; | ||||||
|         if (m_spiral_vase_active) { |         if (m_detect_layer_based_on_tag) { | ||||||
|             if (m_result.moves.empty() || m_result.spiral_vase_layers.empty()) |             if (m_result.moves.empty() || m_result.spiral_vase_layers.empty()) | ||||||
|                 // add a placeholder for layer height. the actual value will be set inside process_G1() method
 |                 // add a placeholder for layer height. the actual value will be set inside process_G1() method
 | ||||||
|                 m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } }); |                 m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } }); | ||||||
|             else { |             else { | ||||||
|                 const size_t move_id = m_result.moves.size() - 1; |                 const size_t move_id = m_result.moves.size() - 1 - m_seams_count; | ||||||
|                 if (!m_result.spiral_vase_layers.empty()) |                 if (!m_result.spiral_vase_layers.empty()) | ||||||
|                     m_result.spiral_vase_layers.back().second.second = move_id; |                     m_result.spiral_vase_layers.back().second.second = move_id; | ||||||
|                 // add a placeholder for layer height. the actual value will be set inside process_G1() method
 |                 // add a placeholder for layer height. the actual value will be set inside process_G1() method
 | ||||||
|  | @ -3215,12 +3225,22 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) | ||||||
|             machine.calculate_time(TimeProcessor::Planner::queue_size); |             machine.calculate_time(TimeProcessor::Planner::queue_size); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     const Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f}; | ||||||
|  | 
 | ||||||
|     if (m_seams_detector.is_active()) { |     if (m_seams_detector.is_active()) { | ||||||
|         // check for seam starting vertex
 |         // check for seam starting vertex
 | ||||||
|         if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) { |         if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { | ||||||
|             //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
 |             //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
 | ||||||
|             const Vec3f real_first_pos = Vec3f(m_result.moves.back().position.x() - m_x_offset, m_result.moves.back().position.y() - m_y_offset, m_result.moves.back().position.z()); |             const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; | ||||||
|             m_seams_detector.set_first_vertex(real_first_pos - m_extruder_offsets[m_extruder_id]); |             if (!m_seams_detector.has_first_vertex()) { | ||||||
|  |                 m_seams_detector.set_first_vertex(new_pos); | ||||||
|  |             } else if (m_detect_layer_based_on_tag) { | ||||||
|  |                 // We may have sloped loop, drop any previous start pos if we have z increment
 | ||||||
|  |                 const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); | ||||||
|  |                 if (new_pos.z() > first_vertex->z()) { | ||||||
|  |                     m_seams_detector.set_first_vertex(new_pos); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         // check for seam ending vertex and store the resulting move
 |         // check for seam ending vertex and store the resulting move
 | ||||||
|         else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) { |         else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) { | ||||||
|  | @ -3230,8 +3250,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) | ||||||
| 
 | 
 | ||||||
|             const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); |             const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); | ||||||
|             //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
 |             //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
 | ||||||
|             const Vec3f real_last_pos = Vec3f(m_result.moves.back().position.x() - m_x_offset, m_result.moves.back().position.y() - m_y_offset, m_result.moves.back().position.z()); |             const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; | ||||||
|             const Vec3f new_pos = real_last_pos - m_extruder_offsets[m_extruder_id]; |  | ||||||
|             const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); |             const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); | ||||||
|             // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
 |             // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
 | ||||||
| 
 | 
 | ||||||
|  | @ -3246,16 +3265,21 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) | ||||||
|     } |     } | ||||||
|     else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { |     else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { | ||||||
|         m_seams_detector.activate(true); |         m_seams_detector.activate(true); | ||||||
|         Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f}; |  | ||||||
|         m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); |         m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty()) { |     if (m_detect_layer_based_on_tag && !m_result.spiral_vase_layers.empty()) { | ||||||
|         if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] >= 0.0) |         if (delta_pos[Z] >= 0.0 && type == EMoveType::Extrude) { | ||||||
|  |             const float current_z = static_cast<float>(m_end_position[Z]); | ||||||
|             // replace layer height placeholder with correct value
 |             // replace layer height placeholder with correct value
 | ||||||
|             m_result.spiral_vase_layers.back().first = static_cast<float>(m_end_position[Z]); |             if (m_result.spiral_vase_layers.back().first == FLT_MAX) { | ||||||
|  |                 m_result.spiral_vase_layers.back().first = current_z; | ||||||
|  |             } else { | ||||||
|  |                 m_result.spiral_vase_layers.back().first = std::max(m_result.spiral_vase_layers.back().first, current_z); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|         if (!m_result.moves.empty()) |         if (!m_result.moves.empty()) | ||||||
|             m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1; |             m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1 - m_seams_count; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // store move
 |     // store move
 | ||||||
|  | @ -3637,8 +3661,17 @@ void  GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) | ||||||
| 
 | 
 | ||||||
|     if (m_seams_detector.is_active()) { |     if (m_seams_detector.is_active()) { | ||||||
|         //BBS: check for seam starting vertex
 |         //BBS: check for seam starting vertex
 | ||||||
|         if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) { |         if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { | ||||||
|             m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); |             const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; | ||||||
|  |             if (!m_seams_detector.has_first_vertex()) { | ||||||
|  |                 m_seams_detector.set_first_vertex(new_pos); | ||||||
|  |             } else if (m_detect_layer_based_on_tag) { | ||||||
|  |                 // We may have sloped loop, drop any previous start pos if we have z increment
 | ||||||
|  |                 const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); | ||||||
|  |                 if (new_pos.z() > first_vertex->z()) { | ||||||
|  |                     m_seams_detector.set_first_vertex(new_pos); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         //BBS: check for seam ending vertex and store the resulting move
 |         //BBS: check for seam ending vertex and store the resulting move
 | ||||||
|         else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) { |         else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) { | ||||||
|  | @ -4267,6 +4300,10 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) | ||||||
|         m_interpolation_points, |         m_interpolation_points, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     if (type == EMoveType::Seam) { | ||||||
|  |         m_seams_count++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // stores stop time placeholders for later use
 |     // stores stop time placeholders for later use
 | ||||||
|     if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) { |     if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) { | ||||||
|         for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { |         for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { | ||||||
|  |  | ||||||
|  | @ -700,7 +700,8 @@ namespace Slic3r { | ||||||
|         SeamsDetector m_seams_detector; |         SeamsDetector m_seams_detector; | ||||||
|         OptionsZCorrector m_options_z_corrector; |         OptionsZCorrector m_options_z_corrector; | ||||||
|         size_t m_last_default_color_id; |         size_t m_last_default_color_id; | ||||||
|         bool m_spiral_vase_active; |         bool m_detect_layer_based_on_tag {false}; | ||||||
|  |         int m_seams_count; | ||||||
| #if ENABLE_GCODE_VIEWER_STATISTICS | #if ENABLE_GCODE_VIEWER_STATISTICS | ||||||
|         std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time; |         std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time; | ||||||
| #endif // ENABLE_GCODE_VIEWER_STATISTICS
 | #endif // ENABLE_GCODE_VIEWER_STATISTICS
 | ||||||
|  | @ -770,6 +771,10 @@ namespace Slic3r { | ||||||
|         //BBS: set offset for gcode writer
 |         //BBS: set offset for gcode writer
 | ||||||
|         void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; } |         void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; } | ||||||
| 
 | 
 | ||||||
|  |         // Orca: if true, only change new layer if ETags::Layer_Change occurs
 | ||||||
|  |         // otherwise when we got a lift of z during extrusion, a new layer will be added
 | ||||||
|  |         void detect_layer_based_on_tag(bool enabled) { m_detect_layer_based_on_tag = enabled; } | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
|         void apply_config(const DynamicPrintConfig& config); |         void apply_config(const DynamicPrintConfig& config); | ||||||
|         void apply_config_simplify3d(const std::string& filename); |         void apply_config_simplify3d(const std::string& filename); | ||||||
|  |  | ||||||
|  | @ -184,7 +184,13 @@ void Layer::make_perimeters() | ||||||
|                         && config.fuzzy_skin                  == other_config.fuzzy_skin |                         && config.fuzzy_skin                  == other_config.fuzzy_skin | ||||||
|                         && config.fuzzy_skin_thickness        == other_config.fuzzy_skin_thickness |                         && config.fuzzy_skin_thickness        == other_config.fuzzy_skin_thickness | ||||||
|                         && config.fuzzy_skin_point_distance       == other_config.fuzzy_skin_point_distance |                         && config.fuzzy_skin_point_distance       == other_config.fuzzy_skin_point_distance | ||||||
|                         && config.fuzzy_skin_first_layer          == other_config.fuzzy_skin_first_layer) |                         && config.fuzzy_skin_first_layer          == other_config.fuzzy_skin_first_layer | ||||||
|  |                         && config.seam_slope_type         == other_config.seam_slope_type | ||||||
|  |                         && config.seam_slope_start_height == other_config.seam_slope_start_height | ||||||
|  |                         && config.seam_slope_entire_loop  == other_config.seam_slope_entire_loop | ||||||
|  |                         && config.seam_slope_min_length   == other_config.seam_slope_min_length | ||||||
|  |                         && config.seam_slope_steps        == other_config.seam_slope_steps | ||||||
|  |                         && config.seam_slope_inner_walls  == other_config.seam_slope_inner_walls) | ||||||
| 		            { | 		            { | ||||||
| 			 			other_layerm->perimeters.clear(); | 			 			other_layerm->perimeters.clear(); | ||||||
| 			 			other_layerm->fills.clear(); | 			 			other_layerm->fills.clear(); | ||||||
|  |  | ||||||
|  | @ -309,6 +309,53 @@ bool Polyline::split_at_index(const size_t index, Polyline* p1, Polyline* p2) co | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool Polyline::split_at_length(const double length, Polyline* p1, Polyline* p2) const | ||||||
|  | { | ||||||
|  |     if (this->points.empty()) return false; | ||||||
|  |     if (length < 0 || length > this->length()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (length < SCALED_EPSILON) { | ||||||
|  |         p1->clear(); | ||||||
|  |         p1->append(this->first_point()); | ||||||
|  |         *p2 = *this; | ||||||
|  |     } else if (is_approx(length, this->length(), SCALED_EPSILON)) { | ||||||
|  |         p2->clear(); | ||||||
|  |         p2->append(this->last_point()); | ||||||
|  |         *p1 = *this; | ||||||
|  |     } else { | ||||||
|  |         // 1 find the line to split at
 | ||||||
|  |         size_t line_idx = 0; | ||||||
|  |         double acc_length = 0; | ||||||
|  |         Point p = this->first_point(); | ||||||
|  |         for (const auto& l : this->lines()) { | ||||||
|  |             p = l.b; | ||||||
|  | 
 | ||||||
|  |             const double current_length = l.length(); | ||||||
|  |             if (acc_length + current_length >= length) { | ||||||
|  |                 p = lerp(l.a, l.b, (length - acc_length) / current_length); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             acc_length += current_length; | ||||||
|  |             line_idx++; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //2 judge whether the cloest point is one vertex of polyline.
 | ||||||
|  |         //  and spilit the polyline at different index
 | ||||||
|  |         int index = this->find_point(p); | ||||||
|  |         if (index != -1) { | ||||||
|  |             this->split_at_index(index, p1, p2); | ||||||
|  |         } else { | ||||||
|  |             Polyline temp; | ||||||
|  |             this->split_at_index(line_idx, p1, &temp); | ||||||
|  |             p1->append(p); | ||||||
|  |             this->split_at_index(line_idx + 1, &temp, p2); | ||||||
|  |             p2->append_before(p); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| bool Polyline::is_straight() const | bool Polyline::is_straight() const | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -130,6 +130,7 @@ public: | ||||||
| //    template <class T> void simplify_by_visibility(const T &area);
 | //    template <class T> void simplify_by_visibility(const T &area);
 | ||||||
|     void split_at(Point &point, Polyline* p1, Polyline* p2) const; |     void split_at(Point &point, Polyline* p1, Polyline* p2) const; | ||||||
|     bool split_at_index(const size_t index, Polyline* p1, Polyline* p2) const; |     bool split_at_index(const size_t index, Polyline* p1, Polyline* p2) const; | ||||||
|  |     bool split_at_length(const double length, Polyline* p1, Polyline* p2) const; | ||||||
| 
 | 
 | ||||||
|     bool is_straight() const; |     bool is_straight() const; | ||||||
|     bool is_closed() const { return this->points.front() == this->points.back(); } |     bool is_closed() const { return this->points.front() == this->points.back(); } | ||||||
|  |  | ||||||
|  | @ -820,6 +820,7 @@ static std::vector<std::string> s_Preset_print_options { | ||||||
|      "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", |      "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", | ||||||
|      "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", |      "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", | ||||||
|      "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", |      "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", | ||||||
|  |      "seam_slope_type", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static std::vector<std::string> s_Preset_filament_options { | static std::vector<std::string> s_Preset_filament_options { | ||||||
|  |  | ||||||
|  | @ -1041,6 +1041,24 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ | ||||||
|     else |     else | ||||||
|         m_support_used = false; |         m_support_used = false; | ||||||
| 
 | 
 | ||||||
|  |     { | ||||||
|  |         const auto& o = model.objects; | ||||||
|  |         const auto opt_has_scarf_joint_seam = [](const DynamicConfig& c) { | ||||||
|  |             return c.has("seam_slope_type") && c.opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None; | ||||||
|  |         }; | ||||||
|  |         const bool has_scarf_joint_seam = std::any_of(o.begin(), o.end(), [&new_full_config, &opt_has_scarf_joint_seam](ModelObject* obj) { | ||||||
|  |             return obj->get_config_value<ConfigOptionEnum<SeamScarfType>>(new_full_config, "seam_slope_type")->value != SeamScarfType::None || | ||||||
|  |                    std::any_of(obj->volumes.begin(), obj->volumes.end(), [&opt_has_scarf_joint_seam](const ModelVolume* v) { return opt_has_scarf_joint_seam(v->config.get());}) || | ||||||
|  |                    std::any_of(obj->layer_config_ranges.begin(), obj->layer_config_ranges.end(), [&opt_has_scarf_joint_seam](const auto& r) { return opt_has_scarf_joint_seam(r.second.get());}); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (has_scarf_joint_seam) { | ||||||
|  |             new_full_config.set("has_scarf_joint_seam", true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", has_scarf_joint_seam:" << has_scarf_joint_seam; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
 |     // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
 | ||||||
|     DynamicPrintConfig   filament_overrides; |     DynamicPrintConfig   filament_overrides; | ||||||
|     //BBS: add plate index
 |     //BBS: add plate index
 | ||||||
|  |  | ||||||
|  | @ -264,6 +264,14 @@ static t_config_enum_values s_keys_map_SeamPosition { | ||||||
| }; | }; | ||||||
| CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition) | CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition) | ||||||
| 
 | 
 | ||||||
|  | // Orca
 | ||||||
|  | static t_config_enum_values s_keys_map_SeamScarfType{ | ||||||
|  |     { "none",           int(SeamScarfType::None) }, | ||||||
|  |     { "external",       int(SeamScarfType::External) }, | ||||||
|  |     { "all",            int(SeamScarfType::All) }, | ||||||
|  | }; | ||||||
|  | CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamScarfType) | ||||||
|  | 
 | ||||||
| // Orca
 | // Orca
 | ||||||
| static t_config_enum_values s_keys_map_InternalBridgeFilter { | static t_config_enum_values s_keys_map_InternalBridgeFilter { | ||||||
|     { "disabled",        ibfDisabled }, |     { "disabled",        ibfDisabled }, | ||||||
|  | @ -2795,6 +2803,10 @@ def = this->add("filament_loading_speed", coFloats); | ||||||
|     def->height = 15; |     def->height = 15; | ||||||
|     def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"}); |     def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"}); | ||||||
| 
 | 
 | ||||||
|  |     def = this->add("has_scarf_joint_seam", coBool); | ||||||
|  |     def->mode = comAdvanced; | ||||||
|  |     def->set_default_value(new ConfigOptionBool(false)); | ||||||
|  | 
 | ||||||
|     { |     { | ||||||
|         struct AxisDefault { |         struct AxisDefault { | ||||||
|             std::string         name; |             std::string         name; | ||||||
|  | @ -3523,6 +3535,55 @@ def = this->add("filament_loading_speed", coFloats); | ||||||
|     def->mode = comAdvanced; |     def->mode = comAdvanced; | ||||||
|     def->set_default_value(new ConfigOptionFloatOrPercent(10,true)); |     def->set_default_value(new ConfigOptionFloatOrPercent(10,true)); | ||||||
| 
 | 
 | ||||||
|  |     def = this->add("seam_slope_type", coEnum); | ||||||
|  |     def->label = L("Scarf joint seam (beta)"); | ||||||
|  |     def->tooltip = L("Use scarf joint to minimize seam visibility and increase seam strength."); | ||||||
|  |     def->enum_keys_map = &ConfigOptionEnum<SeamScarfType>::get_enum_values(); | ||||||
|  |     def->enum_values.push_back("none"); | ||||||
|  |     def->enum_values.push_back("external"); | ||||||
|  |     def->enum_values.push_back("all"); | ||||||
|  |     def->enum_labels.push_back(L("None")); | ||||||
|  |     def->enum_labels.push_back(L("Contour")); | ||||||
|  |     def->enum_labels.push_back(L("Contour and hole")); | ||||||
|  |     def->mode = comAdvanced; | ||||||
|  |     def->set_default_value(new ConfigOptionEnum<SeamScarfType>(SeamScarfType::None)); | ||||||
|  |      | ||||||
|  |     def = this->add("seam_slope_start_height", coFloatOrPercent); | ||||||
|  |     def->label = L("Scarf start height"); | ||||||
|  |     def->tooltip = L("Start height of the scarf.\n" | ||||||
|  |                      "This amount can be specified in millimeters or as a percentage of the current layer height. The default value for this parameter is 0."); | ||||||
|  |     def->sidetext = L("mm or %"); | ||||||
|  |     def->min = 0; | ||||||
|  |     def->mode = comAdvanced; | ||||||
|  |     def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); | ||||||
|  | 
 | ||||||
|  |     def = this->add("seam_slope_entire_loop", coBool); | ||||||
|  |     def->label = L("Scarf around entire wall"); | ||||||
|  |     def->tooltip = L("The scarf extends to the entire length of the wall."); | ||||||
|  |     def->mode = comAdvanced; | ||||||
|  |     def->set_default_value(new ConfigOptionBool(false)); | ||||||
|  | 
 | ||||||
|  |     def = this->add("seam_slope_min_length", coFloat); | ||||||
|  |     def->label = L("Scarf length"); | ||||||
|  |     def->tooltip = L("Length of the scarf. Setting this parameter to zero effectively disables the scarf."); | ||||||
|  |     def->sidetext = L("mm"); | ||||||
|  |     def->min = 0; | ||||||
|  |     def->mode = comAdvanced; | ||||||
|  |     def->set_default_value(new ConfigOptionFloat(20)); | ||||||
|  | 
 | ||||||
|  |     def = this->add("seam_slope_steps", coInt); | ||||||
|  |     def->label = L("Scarf steps"); | ||||||
|  |     def->tooltip = L("Minimum number of segments of each scarf."); | ||||||
|  |     def->min = 1; | ||||||
|  |     def->mode = comAdvanced; | ||||||
|  |     def->set_default_value(new ConfigOptionInt(10)); | ||||||
|  | 
 | ||||||
|  |     def = this->add("seam_slope_inner_walls", coBool); | ||||||
|  |     def->label = L("Scarf joint for inner walls"); | ||||||
|  |     def->tooltip = L("Use scarf joint for inner walls as well."); | ||||||
|  |     def->mode = comAdvanced; | ||||||
|  |     def->set_default_value(new ConfigOptionBool(false)); | ||||||
|  | 
 | ||||||
|     def = this->add("role_based_wipe_speed", coBool); |     def = this->add("role_based_wipe_speed", coBool); | ||||||
|     def->label = L("Role base wipe speed"); |     def->label = L("Role base wipe speed"); | ||||||
|     def->tooltip = L("The wipe speed is determined by the speed of the current extrusion role." |     def->tooltip = L("The wipe speed is determined by the speed of the current extrusion role." | ||||||
|  |  | ||||||
|  | @ -169,6 +169,13 @@ enum SeamPosition { | ||||||
|     spNearest, spAligned, spRear, spRandom |     spNearest, spAligned, spRear, spRandom | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // Orca
 | ||||||
|  | enum class SeamScarfType { | ||||||
|  |     None, | ||||||
|  |     External, | ||||||
|  |     All, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| //Orca
 | //Orca
 | ||||||
| enum InternalBridgeFilter { | enum InternalBridgeFilter { | ||||||
|     ibfDisabled, ibfLimited, ibfNofilter |     ibfDisabled, ibfLimited, ibfNofilter | ||||||
|  | @ -382,6 +389,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportMaterialInterfacePattern) | ||||||
| // BBS
 | // BBS
 | ||||||
| CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportType) | CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportType) | ||||||
| CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamPosition) | CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamPosition) | ||||||
|  | CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamScarfType) | ||||||
| CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLADisplayOrientation) | CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLADisplayOrientation) | ||||||
| CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode) | CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode) | ||||||
| CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) | CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) | ||||||
|  | @ -838,7 +846,6 @@ PRINT_CONFIG_CLASS_DEFINE( | ||||||
|     ((ConfigOptionFloat,              top_surface_jerk)) |     ((ConfigOptionFloat,              top_surface_jerk)) | ||||||
|     ((ConfigOptionFloat,              initial_layer_jerk)) |     ((ConfigOptionFloat,              initial_layer_jerk)) | ||||||
|     ((ConfigOptionFloat,              travel_jerk)) |     ((ConfigOptionFloat,              travel_jerk)) | ||||||
| 
 |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // This object is mapped to Perl as Slic3r::Config::PrintRegion.
 | // This object is mapped to Perl as Slic3r::Config::PrintRegion.
 | ||||||
|  | @ -943,6 +950,14 @@ PRINT_CONFIG_CLASS_DEFINE( | ||||||
|     ((ConfigOptionBool,                is_infill_first)) |     ((ConfigOptionBool,                is_infill_first)) | ||||||
|     ((ConfigOptionBool,                small_area_infill_flow_compensation)) |     ((ConfigOptionBool,                small_area_infill_flow_compensation)) | ||||||
|     ((ConfigOptionEnum<WallDirection>,  wall_direction)) |     ((ConfigOptionEnum<WallDirection>,  wall_direction)) | ||||||
|  | 
 | ||||||
|  |     // Orca: seam slopes
 | ||||||
|  |     ((ConfigOptionEnum<SeamScarfType>,  seam_slope_type)) | ||||||
|  |     ((ConfigOptionFloatOrPercent,       seam_slope_start_height)) | ||||||
|  |     ((ConfigOptionBool,                 seam_slope_entire_loop)) | ||||||
|  |     ((ConfigOptionFloat,                seam_slope_min_length)) | ||||||
|  |     ((ConfigOptionInt,                  seam_slope_steps)) | ||||||
|  |     ((ConfigOptionBool,                 seam_slope_inner_walls)) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| PRINT_CONFIG_CLASS_DEFINE( | PRINT_CONFIG_CLASS_DEFINE( | ||||||
|  | @ -1092,6 +1107,8 @@ PRINT_CONFIG_CLASS_DEFINE( | ||||||
| 
 | 
 | ||||||
|     // Small Area Infill Flow Compensation
 |     // Small Area Infill Flow Compensation
 | ||||||
|     ((ConfigOptionStrings,              small_area_infill_flow_compensation_model)) |     ((ConfigOptionStrings,              small_area_infill_flow_compensation_model)) | ||||||
|  | 
 | ||||||
|  |     ((ConfigOptionBool,                has_scarf_joint_seam)) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // This object is mapped to Perl as Slic3r::Config::Print.
 | // This object is mapped to Perl as Slic3r::Config::Print.
 | ||||||
|  |  | ||||||
|  | @ -1142,6 +1142,12 @@ bool PrintObject::invalidate_state_by_config_options( | ||||||
|             steps.emplace_back(posSlice); |             steps.emplace_back(posSlice); | ||||||
|         } else if ( |         } else if ( | ||||||
|                opt_key == "seam_position" |                opt_key == "seam_position" | ||||||
|  |             || opt_key == "seam_slope_type" | ||||||
|  |             || opt_key == "seam_slope_start_height" | ||||||
|  |             || opt_key == "seam_slope_entire_loop" | ||||||
|  |             || opt_key == "seam_slope_min_length" | ||||||
|  |             || opt_key == "seam_slope_steps" | ||||||
|  |             || opt_key == "seam_slope_inner_walls" | ||||||
|             || opt_key == "support_speed" |             || opt_key == "support_speed" | ||||||
|             || opt_key == "support_interface_speed" |             || opt_key == "support_interface_speed" | ||||||
|             || opt_key == "overhang_1_4_speed" |             || opt_key == "overhang_1_4_speed" | ||||||
|  |  | ||||||
|  | @ -752,6 +752,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co | ||||||
| 
 | 
 | ||||||
|     bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation"); |     bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation"); | ||||||
|     toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation); |     toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation); | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |     toggle_field("seam_slope_type", !has_spiral_vase); | ||||||
|  |     bool has_seam_slope = !has_spiral_vase && config->opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None; | ||||||
|  |     toggle_line("seam_slope_start_height", has_seam_slope); | ||||||
|  |     toggle_line("seam_slope_entire_loop", has_seam_slope); | ||||||
|  |     toggle_line("seam_slope_min_length", has_seam_slope); | ||||||
|  |     toggle_line("seam_slope_steps", has_seam_slope); | ||||||
|  |     toggle_line("seam_slope_inner_walls", has_seam_slope); | ||||||
|  |     toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) | void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) | ||||||
|  |  | ||||||
|  | @ -1976,6 +1976,12 @@ void TabPrint::build() | ||||||
|         optgroup->append_single_option_line("seam_position", "seam"); |         optgroup->append_single_option_line("seam_position", "seam"); | ||||||
|         optgroup->append_single_option_line("staggered_inner_seams", "seam"); |         optgroup->append_single_option_line("staggered_inner_seams", "seam"); | ||||||
|         optgroup->append_single_option_line("seam_gap","seam"); |         optgroup->append_single_option_line("seam_gap","seam"); | ||||||
|  |         optgroup->append_single_option_line("seam_slope_type"); | ||||||
|  |         optgroup->append_single_option_line("seam_slope_start_height"); | ||||||
|  |         optgroup->append_single_option_line("seam_slope_entire_loop"); | ||||||
|  |         optgroup->append_single_option_line("seam_slope_min_length"); | ||||||
|  |         optgroup->append_single_option_line("seam_slope_steps"); | ||||||
|  |         optgroup->append_single_option_line("seam_slope_inner_walls"); | ||||||
|         optgroup->append_single_option_line("role_based_wipe_speed","seam"); |         optgroup->append_single_option_line("role_based_wipe_speed","seam"); | ||||||
|         optgroup->append_single_option_line("wipe_speed", "seam"); |         optgroup->append_single_option_line("wipe_speed", "seam"); | ||||||
|         optgroup->append_single_option_line("wipe_on_loops","seam"); |         optgroup->append_single_option_line("wipe_on_loops","seam"); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Noisyfox
						Noisyfox