mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	ENH: fix hang issue for archimedean chords pattern
This is fix for STUDIO-2079 that slicer may hang when generated archimedean chords pattern lines. Thanks prusa. Signed-off-by: salt.wei <salt.wei@bambulab.com> Change-Id: I923c1ef48f093b2ab9576cb04beb4787c903ca00
This commit is contained in:
		
							parent
							
								
									223291452b
								
							
						
					
					
						commit
						03e031fe5c
					
				
					 4 changed files with 186 additions and 63 deletions
				
			
		|  | @ -867,6 +867,8 @@ Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygon | |||
|     { return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); } | ||||
| Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip) | ||||
|     { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); } | ||||
| Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip) | ||||
|     { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); } | ||||
| Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip) | ||||
|     { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); } | ||||
| Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip) | ||||
|  |  | |||
|  | @ -494,7 +494,8 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r | |||
| Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); | ||||
| Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); | ||||
| Slic3r::Polylines  intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); | ||||
| Slic3r::Polylines  intersection_pl(const Slic3r::Polylines& subject, const Slic3r::ExPolygon& clip); | ||||
| Slic3r::Polylines  intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); | ||||
| Slic3r::Polylines  intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); | ||||
| Slic3r::Polylines  intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip); | ||||
| Slic3r::Polylines  intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); | ||||
| Slic3r::Polylines  intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip); | ||||
|  |  | |||
|  | @ -6,6 +6,66 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class InfillPolylineClipper : public InfillPolylineOutput { | ||||
| public: | ||||
|     InfillPolylineClipper(const BoundingBox bbox, const double scale_out) : InfillPolylineOutput(scale_out), m_bbox(bbox) {} | ||||
| 
 | ||||
|     void            add_point(const Vec2d &pt); | ||||
|     Points&&        result() { return std::move(m_out); } | ||||
|     bool            clips() const override { return true; } | ||||
| 
 | ||||
| private: | ||||
|     enum class Side { | ||||
|         Left   = 1, | ||||
|         Right  = 2, | ||||
|         Top    = 4, | ||||
|         Bottom = 8 | ||||
|     }; | ||||
| 
 | ||||
|     int sides(const Point &p) const { | ||||
|         return int(p.x() < m_bbox.min.x()) * int(Side::Left) + | ||||
|                int(p.x() > m_bbox.max.x()) * int(Side::Right) + | ||||
|                int(p.y() < m_bbox.min.y()) * int(Side::Bottom) + | ||||
|                int(p.y() > m_bbox.max.y()) * int(Side::Top); | ||||
|     }; | ||||
| 
 | ||||
|     // Bounding box to clip the polyline with.
 | ||||
|     BoundingBox m_bbox; | ||||
| 
 | ||||
|     // Classification of the two last points processed.
 | ||||
|     int         m_sides_prev; | ||||
|     int         m_sides_this; | ||||
| }; | ||||
| 
 | ||||
| void InfillPolylineClipper::add_point(const Vec2d &fpt) | ||||
| { | ||||
|     const Point pt{ this->scaled(fpt) }; | ||||
| 
 | ||||
|     if (m_out.size() < 2) { | ||||
|         // Collect the two first points and their status.
 | ||||
|         (m_out.empty() ? m_sides_prev : m_sides_this) = sides(pt); | ||||
|         m_out.emplace_back(pt); | ||||
|     } else { | ||||
|         // Classify the last inserted point, possibly remove it.
 | ||||
|         int sides_next = sides(pt); | ||||
|         if (// This point is inside. Take it.
 | ||||
|             m_sides_this == 0 || | ||||
|             // Either this point is outside and previous or next is inside, or
 | ||||
|             // the edge possibly cuts corner of the bounding box.
 | ||||
|             (m_sides_prev & m_sides_this & sides_next) == 0) { | ||||
|             // Keep the last point.
 | ||||
|             m_sides_prev = m_sides_this; | ||||
|         } else { | ||||
|             // All the three points (this, prev, next) are outside at the same side.
 | ||||
|             // Ignore the last point.
 | ||||
|             m_out.pop_back(); | ||||
|         } | ||||
|         // And save the current point.
 | ||||
|         m_out.emplace_back(pt); | ||||
|         m_sides_this = sides_next; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void FillPlanePath::_fill_surface_single( | ||||
|     const FillParams                ¶ms,  | ||||
|     unsigned int                     thickness_layers, | ||||
|  | @ -13,37 +73,52 @@ void FillPlanePath::_fill_surface_single( | |||
|     ExPolygon                        expolygon, | ||||
|     Polylines                       &polylines_out) | ||||
| { | ||||
|     expolygon.rotate(- direction.first); | ||||
|     expolygon.rotate(-direction.first); | ||||
| 
 | ||||
| 	coord_t distance_between_lines = coord_t(scale_(this->spacing) / params.density); | ||||
|      | ||||
|     // align infill across layers using the object's bounding box
 | ||||
|     // Rotated bounding box of the whole object.
 | ||||
|     BoundingBox bounding_box = this->bounding_box.rotated(- direction.first); | ||||
|      | ||||
|     Point shift = this->_centered() ?  | ||||
|     //FIXME Vojtech: We are not sure whether the user expects the fill patterns on visible surfaces to be aligned across all the islands of a single layer.
 | ||||
|     // One may align for this->centered() to align the patterns for Archimedean Chords and Octagram Spiral patterns.
 | ||||
|     const bool align = params.density < 0.995; | ||||
| 
 | ||||
|     BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON); | ||||
| 
 | ||||
|     // Rotated bounding box of the area to fill in with the pattern.
 | ||||
|     BoundingBox bounding_box = align ? | ||||
|         // Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box.
 | ||||
|         this->bounding_box.rotated(-direction.first) : | ||||
|         // Solid infill does not need to be aligned across layers, generate the infill pattern
 | ||||
|         // around the clipping expolygon only.
 | ||||
|         snug_bounding_box; | ||||
| 
 | ||||
|     Point shift = this->centered() ?  | ||||
|         bounding_box.center() : | ||||
|         bounding_box.min; | ||||
|     expolygon.translate(-shift.x(), -shift.y()); | ||||
|     bounding_box.translate(-shift.x(), -shift.y()); | ||||
| 
 | ||||
|     Pointfs pts = _generate( | ||||
|         coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)), | ||||
|         coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)), | ||||
|         coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)), | ||||
|         coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)), | ||||
|         params.resolution); | ||||
|     Polyline polyline; | ||||
|     { | ||||
|         auto distance_between_lines = scaled<double>(this->spacing) / params.density; | ||||
|         auto min_x = coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)); | ||||
|         auto min_y = coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)); | ||||
|         auto max_x = coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)); | ||||
|         auto max_y = coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)); | ||||
|         auto resolution = scaled<double>(params.resolution) / distance_between_lines; | ||||
|         if (align) { | ||||
|             // Filling in a bounding box over the whole object, clip generated polyline against the snug bounding box.
 | ||||
|             snug_bounding_box.translate(-shift.x(), -shift.y()); | ||||
|             InfillPolylineClipper output(snug_bounding_box, distance_between_lines); | ||||
|             this->generate(min_x, min_y, max_x, max_y, resolution, output); | ||||
|             polyline.points = std::move(output.result()); | ||||
|         } else { | ||||
|             // Filling in a snug bounding box, no need to clip.
 | ||||
|             InfillPolylineOutput output(distance_between_lines); | ||||
|             this->generate(min_x, min_y, max_x, max_y, resolution, output); | ||||
|             polyline.points = std::move(output.result()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (pts.size() >= 2) { | ||||
|         // Convert points to a polyline, upscale.
 | ||||
|         Polylines polylines(1, Polyline()); | ||||
|         Polyline &polyline = polylines.front(); | ||||
|         polyline.points.reserve(pts.size()); | ||||
|         for (const Vec2d &pt : pts) | ||||
|             polyline.points.emplace_back( | ||||
|                 coord_t(floor(pt.x() * distance_between_lines + 0.5)), | ||||
|                 coord_t(floor(pt.y() * distance_between_lines + 0.5))); | ||||
|         polylines = intersection_pl(polylines, expolygon); | ||||
|     if (polyline.size() >= 2) { | ||||
|         Polylines polylines = intersection_pl(polyline, expolygon); | ||||
|         Polylines chained; | ||||
|         if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1) | ||||
|             chained = chain_polylines(std::move(polylines)); | ||||
|  | @ -59,7 +134,8 @@ void FillPlanePath::_fill_surface_single( | |||
| } | ||||
| 
 | ||||
| // Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
 | ||||
| Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) | ||||
| template<typename Output> | ||||
| static void generate_archimedean_chords(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, Output &output) | ||||
| { | ||||
|     // Radius to achieve.
 | ||||
|     coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; | ||||
|  | @ -70,15 +146,22 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m | |||
|     coordf_t r = 1; | ||||
|     Pointfs out; | ||||
|     //FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
 | ||||
|     out.emplace_back(0, 0); | ||||
|     out.emplace_back(1, 0); | ||||
|     output.add_point({ 0, 0 }); | ||||
|     output.add_point({ 1, 0 }); | ||||
|     while (r < rmax) { | ||||
|         // Discretization angle to achieve a discretization error lower than resolution.
 | ||||
|         theta += 2. * acos(1. - resolution / r); | ||||
|         r = a + b * theta; | ||||
|         out.emplace_back(r * cos(theta), r * sin(theta)); | ||||
|         output.add_point({ r * cos(theta), r * sin(theta) }); | ||||
|     } | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| void FillArchimedeanChords::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) | ||||
| { | ||||
|     if (output.clips()) | ||||
|         generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, static_cast<InfillPolylineClipper&>(output)); | ||||
|     else | ||||
|         generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, output); | ||||
| } | ||||
| 
 | ||||
| // Adapted from 
 | ||||
|  | @ -126,7 +209,8 @@ static inline Point hilbert_n_to_xy(const size_t n) | |||
|     return Point(x, y); | ||||
| } | ||||
| 
 | ||||
| Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */) | ||||
| template<typename Output> | ||||
| static void generate_hilbert_curve(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output) | ||||
| { | ||||
|     // Minimum power of two square to fit the domain.
 | ||||
|     size_t sz = 2; | ||||
|  | @ -140,46 +224,59 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, | |||
|     } | ||||
| 
 | ||||
|     size_t sz2 = sz * sz; | ||||
|     Pointfs line; | ||||
|     line.reserve(sz2); | ||||
|     output.reserve(sz2); | ||||
|     for (size_t i = 0; i < sz2; ++ i) { | ||||
|         Point p = hilbert_n_to_xy(i); | ||||
|         line.emplace_back(p.x() + min_x, p.y() + min_y); | ||||
|         output.add_point({ p.x() + min_x, p.y() + min_y }); | ||||
|     } | ||||
|     return line; | ||||
| } | ||||
| 
 | ||||
| Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */) | ||||
| void FillHilbertCurve::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output) | ||||
| { | ||||
|     if (output.clips()) | ||||
|         generate_hilbert_curve(min_x, min_y, max_x, max_y, static_cast<InfillPolylineClipper&>(output)); | ||||
|     else | ||||
|         generate_hilbert_curve(min_x, min_y, max_x, max_y, output); | ||||
| } | ||||
| 
 | ||||
| template<typename Output> | ||||
| static void generate_octagram_spiral(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output) | ||||
| { | ||||
|     // Radius to achieve.
 | ||||
|     coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; | ||||
|     // Now unwind the spiral.
 | ||||
|     coordf_t r = 0; | ||||
|     coordf_t r_inc = sqrt(2.); | ||||
|     Pointfs out; | ||||
|     out.emplace_back(0., 0.); | ||||
|     output.add_point({ 0., 0. }); | ||||
|     while (r < rmax) { | ||||
|         r += r_inc; | ||||
|         coordf_t rx = r / sqrt(2.); | ||||
|         coordf_t r2 = r + rx; | ||||
|         out.emplace_back( r,  0.); | ||||
|         out.emplace_back( r2, rx); | ||||
|         out.emplace_back( rx, rx); | ||||
|         out.emplace_back( rx, r2); | ||||
|         out.emplace_back( 0.,  r); | ||||
|         out.emplace_back(-rx, r2); | ||||
|         out.emplace_back(-rx, rx); | ||||
|         out.emplace_back(-r2, rx); | ||||
|         out.emplace_back(- r, 0.); | ||||
|         out.emplace_back(-r2, -rx); | ||||
|         out.emplace_back(-rx, -rx); | ||||
|         out.emplace_back(-rx, -r2); | ||||
|         out.emplace_back( 0., -r); | ||||
|         out.emplace_back( rx, -r2); | ||||
|         out.emplace_back( rx, -rx); | ||||
|         out.emplace_back( r2+r_inc, -rx); | ||||
|         output.add_point({ r,   0. }); | ||||
|         output.add_point({ r2,  rx }); | ||||
|         output.add_point({ rx,  rx }); | ||||
|         output.add_point({ rx,  r2 }); | ||||
|         output.add_point({ 0.,  r  }); | ||||
|         output.add_point({-rx,  r2 }); | ||||
|         output.add_point({-rx,  rx }); | ||||
|         output.add_point({-r2,  rx }); | ||||
|         output.add_point({- r,  0. }); | ||||
|         output.add_point({-r2, -rx }); | ||||
|         output.add_point({-rx, -rx }); | ||||
|         output.add_point({-rx, -r2 }); | ||||
|         output.add_point({ 0., -r  }); | ||||
|         output.add_point({ rx, -r2 }); | ||||
|         output.add_point({ rx, -rx }); | ||||
|         output.add_point({ r2+r_inc, -rx }); | ||||
|     } | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| void FillOctagramSpiral::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output) | ||||
| { | ||||
|     if (output.clips()) | ||||
|         generate_octagram_spiral(min_x, min_y, max_x, max_y, static_cast<InfillPolylineClipper&>(output)); | ||||
|     else | ||||
|         generate_octagram_spiral(min_x, min_y, max_x, max_y, output); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -13,6 +13,26 @@ namespace Slic3r { | |||
| // http://user42.tuxfamily.org/math-planepath/
 | ||||
| // http://user42.tuxfamily.org/math-planepath/gallery.html
 | ||||
| 
 | ||||
| class InfillPolylineOutput { | ||||
| public: | ||||
|     InfillPolylineOutput(const double scale_out) : m_scale_out(scale_out) {} | ||||
| 
 | ||||
|     void            reserve(size_t n) { m_out.reserve(n); } | ||||
|     void            add_point(const Vec2d& pt) { m_out.emplace_back(this->scaled(pt)); } | ||||
|     Points&& result() { return std::move(m_out); } | ||||
|     virtual bool    clips() const { return false; } | ||||
| 
 | ||||
| protected: | ||||
|     const Point     scaled(const Vec2d& fpt) const { return { coord_t(floor(fpt.x() * m_scale_out + 0.5)), coord_t(floor(fpt.y() * m_scale_out + 0.5)) }; } | ||||
| 
 | ||||
|     // Output polyline.
 | ||||
|     Points          m_out; | ||||
| 
 | ||||
| private: | ||||
|     // Scaling coefficient of the generated points before tested against m_bbox and clipped by bbox.
 | ||||
|     double          m_scale_out; | ||||
| }; | ||||
| 
 | ||||
| class FillPlanePath : public Fill | ||||
| { | ||||
| public: | ||||
|  | @ -27,8 +47,11 @@ protected: | |||
|         Polylines                       &polylines_out) override; | ||||
| 
 | ||||
|     float _layer_angle(size_t idx) const override { return 0.f; } | ||||
|     virtual bool  _centered() const = 0; | ||||
|     virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) = 0; | ||||
|     virtual bool centered() const = 0; | ||||
| 
 | ||||
|     friend class InfillPolylineClipper; | ||||
|      | ||||
|     virtual void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) = 0; | ||||
| }; | ||||
| 
 | ||||
| class FillArchimedeanChords : public FillPlanePath | ||||
|  | @ -38,8 +61,8 @@ public: | |||
|     ~FillArchimedeanChords() override = default; | ||||
| 
 | ||||
| protected: | ||||
|     bool  _centered() const override { return true; } | ||||
|     Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; | ||||
|     bool centered() const override { return true; } | ||||
|     void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override; | ||||
| }; | ||||
| 
 | ||||
| class FillHilbertCurve : public FillPlanePath | ||||
|  | @ -49,8 +72,8 @@ public: | |||
|     ~FillHilbertCurve() override = default; | ||||
| 
 | ||||
| protected: | ||||
|     bool  _centered() const override { return false; } | ||||
|     Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; | ||||
|     bool centered() const override { return false; } | ||||
|     void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override; | ||||
| }; | ||||
| 
 | ||||
| class FillOctagramSpiral : public FillPlanePath | ||||
|  | @ -60,8 +83,8 @@ public: | |||
|     ~FillOctagramSpiral() override = default; | ||||
| 
 | ||||
| protected: | ||||
|     bool  _centered() const override { return true; } | ||||
|     Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; | ||||
|     bool centered() const override { return true; } | ||||
|     void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 salt.wei
						salt.wei