mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Disabling wipe for avoid crossing perimeters
This commit is contained in:
		
							parent
							
								
									7f94e9fa59
								
							
						
					
					
						commit
						ef9de07740
					
				
					 4 changed files with 119 additions and 32 deletions
				
			
		|  | @ -2662,14 +2662,16 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string | |||
|     travel.append(point); | ||||
| 
 | ||||
|     // check whether a straight travel move would need retraction
 | ||||
|     bool needs_retraction = this->needs_retraction(travel, role); | ||||
|     bool needs_retraction       = this->needs_retraction(travel, role); | ||||
|     // check whether wipe could be disabled without causing visible stringing
 | ||||
|     bool could_be_wipe_disabled = false; | ||||
| 
 | ||||
|     // if a retraction would be needed, try to use avoid_crossing_perimeters to plan a
 | ||||
|     // multi-hop travel path inside the configuration space
 | ||||
|     if (needs_retraction | ||||
|         && m_config.avoid_crossing_perimeters | ||||
|         && ! m_avoid_crossing_perimeters.disable_once) { | ||||
|         travel = m_avoid_crossing_perimeters.travel_to(*this, point); | ||||
|         travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled); | ||||
| 
 | ||||
|         // check again whether the new travel path still needs a retraction
 | ||||
|         needs_retraction = this->needs_retraction(travel, role); | ||||
|  | @ -2683,6 +2685,9 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string | |||
|     // generate G-code for the travel move
 | ||||
|     std::string gcode; | ||||
|     if (needs_retraction) { | ||||
|         if (m_config.avoid_crossing_perimeters && !m_avoid_crossing_perimeters.disable_once && could_be_wipe_disabled) | ||||
|             m_wipe.reset_path(); | ||||
| 
 | ||||
|         Point last_post_before_retract = this->last_pos(); | ||||
|         gcode += this->retract(); | ||||
|         // When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
 | ||||
|  |  | |||
|  | @ -156,6 +156,7 @@ public: | |||
|     const FullPrintConfig &config() const { return m_config; } | ||||
|     const Layer*    layer() const { return m_layer; } | ||||
|     GCodeWriter&    writer() { return m_writer; } | ||||
|     const GCodeWriter& writer() const { return m_writer; } | ||||
|     PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } | ||||
|     const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } | ||||
|     // Process a template through the placeholder parser, collect error messages to be reported
 | ||||
|  |  | |||
|  | @ -115,6 +115,7 @@ static Matrix2d rotation_by_direction(const Point &direction) | |||
| 
 | ||||
| static Point find_first_different_vertex(const Polygon &polygon, const size_t point_idx, const Point &point, bool forward) | ||||
| { | ||||
|     assert(point_idx < polygon.size()); | ||||
|     if (point != polygon.points[point_idx]) | ||||
|         return polygon.points[point_idx]; | ||||
| 
 | ||||
|  | @ -137,8 +138,9 @@ static Vec2d three_points_inward_normal(const Point &left, const Point &middle, | |||
|     normal_2.normalize(); | ||||
| 
 | ||||
|     return (normal_1 + normal_2).normalized(); | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| // Compute normal of the polygon's vertex in an inward direction
 | ||||
| static Vec2d get_polygon_vertex_inward_normal(const Polygon &polygon, const size_t point_idx) | ||||
| { | ||||
|     const size_t left_idx  = (point_idx <= 0) ? (polygon.size() - 1) : (point_idx - 1); | ||||
|  | @ -149,12 +151,13 @@ static Vec2d get_polygon_vertex_inward_normal(const Polygon &polygon, const size | |||
|     return three_points_inward_normal(left, middle, right); | ||||
| } | ||||
| 
 | ||||
| // Compute offset of polygon's in a direction inward normal
 | ||||
| // Compute offset of point_idx of the polygon in a direction of inward normal
 | ||||
| static Point get_polygon_vertex_offset(const Polygon &polygon, const size_t point_idx, const int offset) | ||||
| { | ||||
|     return polygon.points[point_idx] + (get_polygon_vertex_inward_normal(polygon, point_idx) * double(offset)).cast<coord_t>(); | ||||
| } | ||||
| 
 | ||||
| // Compute offset (in the direction of inward normal) of the point(passed on "middle") based on the nearest points laying on the polygon (left_idx and right_idx).
 | ||||
| static Point get_middle_point_offset(const Polygon &polygon, const size_t left_idx, const size_t right_idx, const Point &middle, const int offset) | ||||
| { | ||||
|     const Point &left  = find_first_different_vertex(polygon, left_idx, middle, false); | ||||
|  | @ -243,6 +246,15 @@ static coord_t get_perimeter_spacing_external(const Layer &layer) | |||
|     return perimeter_spacing; | ||||
| } | ||||
| 
 | ||||
| // Check if anyone of ExPolygons contains whole travel.
 | ||||
| template<class T> static bool any_expolygon_contains(const ExPolygons &ex_polygons, const T &travel) | ||||
| { | ||||
|     for (const ExPolygon &ex_polygon : ex_polygons) | ||||
|         if (ex_polygon.contains(travel)) return true; | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| ExPolygons AvoidCrossingPerimeters2::get_boundary(const Layer &layer) | ||||
| { | ||||
|     const coord_t perimeter_spacing = get_perimeter_spacing(layer); | ||||
|  | @ -321,6 +333,7 @@ ExPolygons AvoidCrossingPerimeters2::get_boundary_external(const Layer &layer) | |||
| 
 | ||||
|     Polygons contours; | ||||
|     Polygons holes; | ||||
|     contours.reserve(boundary.size()); | ||||
|     for (ExPolygon &poly : boundary) { | ||||
|         contours.emplace_back(poly.contour); | ||||
|         append(holes, poly.holes); | ||||
|  | @ -429,10 +442,11 @@ Polyline AvoidCrossingPerimeters2::simplify_travel(const EdgeGrid::Grid &edge_gr | |||
|     return optimized_comb_path; | ||||
| } | ||||
| 
 | ||||
| Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons       &boundaries, | ||||
|                                                     const EdgeGrid::Grid &edge_grid, | ||||
|                                                     const Point          &start, | ||||
|                                                     const Point          &end) | ||||
| size_t AvoidCrossingPerimeters2::avoid_perimeters(const Polygons       &boundaries, | ||||
|                                                   const EdgeGrid::Grid &edge_grid, | ||||
|                                                   const Point          &start, | ||||
|                                                   const Point          &end, | ||||
|                                                   Polyline             *result_out) | ||||
| { | ||||
|     const Point direction           = end - start; | ||||
|     Matrix2d    transform_to_x_axis = rotation_by_direction(direction); | ||||
|  | @ -496,12 +510,12 @@ Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons       &bounda | |||
|                 // Append the nearest intersection into the path
 | ||||
|                 size_t left_idx  = intersection_first.line_idx; | ||||
|                 size_t right_idx = (intersection_first.line_idx >= (boundaries[intersection_first.border_idx].points.size() - 1)) ? 0 : (intersection_first.line_idx + 1); | ||||
|                 // Offset of the polygon's point using get_middle_point_offset is used to simplify calculation of intersection between boundary
 | ||||
|                 result.append(get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, SCALED_EPSILON)); | ||||
| 
 | ||||
|                 Direction shortest_direction = get_shortest_direction(border_lines, intersection_first.line_idx, intersection_second.line_idx, | ||||
|                                                                       intersection_first.point, intersection_second.point); | ||||
|                 // Append the path around the border into the path
 | ||||
|                 // Offset of the polygon's point is used to simplify calculation of intersection between boundary
 | ||||
|                 if (shortest_direction == Direction::Forward) | ||||
|                     for (int line_idx = intersection_first.line_idx; line_idx != int(intersection_second.line_idx); | ||||
|                          line_idx     = (((line_idx + 1) < int(border_lines.size())) ? (line_idx + 1) : 0)) | ||||
|  | @ -524,10 +538,48 @@ Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons       &bounda | |||
|     } | ||||
| 
 | ||||
|     result.append(end); | ||||
|     return simplify_travel(edge_grid, result); | ||||
|     if(!intersections.empty()) { | ||||
|         result = simplify_travel(edge_grid, result); | ||||
|     } | ||||
| 
 | ||||
|     append(result_out->points, result.points); | ||||
|     return intersections.size(); | ||||
| } | ||||
| 
 | ||||
| Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point &point) | ||||
| bool AvoidCrossingPerimeters2::needs_wipe(const GCode &   gcodegen, | ||||
|                                           const Line &    original_travel, | ||||
|                                           const Polyline &result_travel, | ||||
|                                           const size_t    intersection_count) | ||||
| { | ||||
|     bool z_lift_enabled = gcodegen.config().retract_lift.get_at(gcodegen.writer().extruder()->id()) > 0.; | ||||
|     bool wipe_needed    = false; | ||||
| 
 | ||||
|     // If the original unmodified path doesn't have any intersection with boundary, then it is entirely inside the object otherwise is entirely
 | ||||
|     // outside the object.
 | ||||
|     if (intersection_count > 0) { | ||||
|         // The original layer is intersected with defined boundaries. Then it is necessary to make a detailed test.
 | ||||
|         // If the z-lift is enabled, then a wipe is needed when the original travel leads above the holes.
 | ||||
|         if (z_lift_enabled) { | ||||
|             if (any_expolygon_contains(m_slice, original_travel)) { | ||||
|                 // Check if original_travel and are not same result_travel
 | ||||
|                 if (result_travel.size() == 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) { | ||||
|                     wipe_needed = false; | ||||
|                 } else { | ||||
|                     wipe_needed = !any_expolygon_contains(m_slice, result_travel); | ||||
|                 } | ||||
|             } else { | ||||
|                 wipe_needed = true; | ||||
|             } | ||||
|         } else { | ||||
|             wipe_needed = !any_expolygon_contains(m_slice, result_travel); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return wipe_needed; | ||||
| } | ||||
| 
 | ||||
| // Plan travel, which avoids perimeter crossings by following the boundaries of the layer.
 | ||||
| Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) | ||||
| { | ||||
|     // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset).
 | ||||
|     // Otherwise perform the path planning in the coordinate system of the active object.
 | ||||
|  | @ -536,14 +588,16 @@ Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point | |||
|     Point    start         = gcodegen.last_pos() + scaled_origin; | ||||
|     Point    end           = point + scaled_origin; | ||||
|     Polyline result; | ||||
|     size_t   travel_intersection_count = 0; | ||||
|     if (!check_if_could_cross_perimeters(use_external ? m_bbox_external : m_bbox, start, end)) { | ||||
|         result = Polyline({start, end}); | ||||
|         result                    = Polyline({start, end}); | ||||
|         travel_intersection_count = 0; | ||||
|     } else { | ||||
|         auto [start_clamped, end_clamped] = clamp_endpoints_by_bounding_box(use_external ? m_bbox_external : m_bbox, start, end); | ||||
|         if (use_external) | ||||
|             result = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped); | ||||
|             travel_intersection_count = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped, &result); | ||||
|         else | ||||
|             result = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped); | ||||
|             travel_intersection_count = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped, &result); | ||||
|     } | ||||
| 
 | ||||
|     result.points.front() = start; | ||||
|  | @ -554,33 +608,32 @@ Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point | |||
|     if ((max_detour_length > 0) && ((result.length() - travel.length()) > max_detour_length)) { | ||||
|         result = Polyline({start, end}); | ||||
|     } | ||||
|     if (use_external) | ||||
|     if (use_external) { | ||||
|         result.translate(-scaled_origin); | ||||
|         *could_be_wipe_disabled = false; | ||||
|     } else | ||||
|         *could_be_wipe_disabled = !needs_wipe(gcodegen, travel, result, travel_intersection_count); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void AvoidCrossingPerimeters2::init_layer(const Layer &layer) | ||||
| { | ||||
|     m_slice.clear(); | ||||
|     m_boundaries.clear(); | ||||
|     m_boundaries_external.clear(); | ||||
| 
 | ||||
|     ExPolygons boundaries          = get_boundary(layer); | ||||
|     ExPolygons boundaries_external = get_boundary_external(layer); | ||||
|     for (const LayerRegion *layer_region : layer.regions()) | ||||
|         append(m_slice, (ExPolygons) layer_region->slices); | ||||
| 
 | ||||
|     m_bbox = get_extents(boundaries); | ||||
|     m_boundaries = to_polygons(get_boundary(layer)); | ||||
|     m_boundaries_external = to_polygons(get_boundary_external(layer)); | ||||
| 
 | ||||
|     m_bbox = get_extents(m_boundaries); | ||||
|     m_bbox.offset(SCALED_EPSILON); | ||||
|     m_bbox_external = get_extents(boundaries_external); | ||||
|     m_bbox_external = get_extents(m_boundaries_external); | ||||
|     m_bbox_external.offset(SCALED_EPSILON); | ||||
| 
 | ||||
|     for (const ExPolygon &ex_poly : boundaries) { | ||||
|         m_boundaries.emplace_back(ex_poly.contour); | ||||
|         append(m_boundaries, ex_poly.holes); | ||||
|     } | ||||
|     for (const ExPolygon &ex_poly : boundaries_external) { | ||||
|         m_boundaries_external.emplace_back(ex_poly.contour); | ||||
|         append(m_boundaries_external, ex_poly.holes); | ||||
|     } | ||||
| 
 | ||||
|     m_grid.set_bbox(m_bbox); | ||||
|     m_grid.create(m_boundaries, scale_(1.)); | ||||
|     m_grid_external.set_bbox(m_bbox_external); | ||||
|  |  | |||
|  | @ -37,11 +37,17 @@ public: | |||
|         m_external_mp.reset(); | ||||
|         m_layer_mp.reset(); | ||||
|     } | ||||
|     void init_external_mp(const Print &print); | ||||
|     void init_layer_mp(const ExPolygons &islands) { m_layer_mp = Slic3r::make_unique<MotionPlanner>(islands); } | ||||
|     virtual void init_external_mp(const Print &print); | ||||
|     virtual void init_layer_mp(const ExPolygons &islands) { m_layer_mp = Slic3r::make_unique<MotionPlanner>(islands); } | ||||
| 
 | ||||
|     virtual Polyline travel_to(const GCode &gcodegen, const Point &point); | ||||
| 
 | ||||
|     virtual Polyline travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) | ||||
|     { | ||||
|         *could_be_wipe_disabled = true; | ||||
|         return this->travel_to(gcodegen, point); | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     // For initializing the regions to avoid.
 | ||||
|     static Polygons collect_contours_all_layers(const PrintObjectPtrs &objects); | ||||
|  | @ -55,9 +61,13 @@ class AvoidCrossingPerimeters2 : public AvoidCrossingPerimeters | |||
| protected: | ||||
|     struct Intersection | ||||
|     { | ||||
|         // Index of the polygon containing this point of intersection.
 | ||||
|         size_t border_idx; | ||||
|         // Index of the line on the polygon containing this point of intersection.
 | ||||
|         size_t line_idx; | ||||
|         // Point of intersection projected on the travel path.
 | ||||
|         Point  point_transformed; | ||||
|         // Point of intersection.
 | ||||
|         Point  point; | ||||
| 
 | ||||
|         Intersection(size_t border_idx, size_t line_idx, const Point &point_transformed, const Point &point) | ||||
|  | @ -78,11 +88,19 @@ private: | |||
| 
 | ||||
|     static Polyline simplify_travel(const EdgeGrid::Grid &edge_grid, const Polyline &travel); | ||||
| 
 | ||||
|     static Polyline avoid_perimeters(const Polygons &boundaries, const EdgeGrid::Grid &grid, const Point &start, const Point &end); | ||||
|     static size_t avoid_perimeters(const Polygons &boundaries, const EdgeGrid::Grid &grid, const Point &start, const Point &end, Polyline *result_out); | ||||
| 
 | ||||
|     bool needs_wipe(const GCode &gcodegen, const Line &original_travel, const Polyline &result_travel, const size_t intersection_count); | ||||
| 
 | ||||
|     // Slice of layer with elephant foot compensation
 | ||||
|     ExPolygons     m_slice; | ||||
|     // Collection of boundaries used for detection of crossing perimetrs for travels inside object
 | ||||
|     Polygons       m_boundaries; | ||||
|     // Collection of boundaries used for detection of crossing perimetrs for travels outside object
 | ||||
|     Polygons       m_boundaries_external; | ||||
|     // Bounding box of m_boundaries
 | ||||
|     BoundingBox    m_bbox; | ||||
|     // Bounding box of m_boundaries_external
 | ||||
|     BoundingBox    m_bbox_external; | ||||
|     EdgeGrid::Grid m_grid; | ||||
|     EdgeGrid::Grid m_grid_external; | ||||
|  | @ -92,7 +110,17 @@ public: | |||
| 
 | ||||
|     virtual ~AvoidCrossingPerimeters2() = default; | ||||
| 
 | ||||
|     virtual Polyline travel_to(const GCode &gcodegen, const Point &point) override; | ||||
|     // Used for disabling unnecessary calling collect_contours_all_layers
 | ||||
|     virtual void init_external_mp(const Print &print) override {}; | ||||
|     virtual void init_layer_mp(const ExPolygons &islands) override {}; | ||||
| 
 | ||||
|     virtual Polyline travel_to(const GCode &gcodegen, const Point &point) override | ||||
|     { | ||||
|         bool could_be_wipe_disabled; | ||||
|         return this->travel_to(gcodegen, point, &could_be_wipe_disabled); | ||||
|     } | ||||
| 
 | ||||
|     virtual Polyline travel_to(const GCode &gcodegen, const Point &point, bool *needs_wipe) override; | ||||
| 
 | ||||
|     void init_layer(const Layer &layer); | ||||
| }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukáš Hejl
						Lukáš Hejl