1 #ifndef slic3r_ExtrusionEntity_hpp_
2 #define slic3r_ExtrusionEntity_hpp_
3 
4 #include "libslic3r.h"
5 #include "Polygon.hpp"
6 #include "Polyline.hpp"
7 
8 #include <assert.h>
9 #include <string_view>
10 
11 namespace Slic3r {
12 
13 class ExPolygonCollection;
14 class ExtrusionEntityCollection;
15 class Extruder;
16 
17 // Each ExtrusionRole value identifies a distinct set of { extruder, speed }
18 enum ExtrusionRole : uint8_t {
19     erNone,
20     erPerimeter,
21     erExternalPerimeter,
22     erOverhangPerimeter,
23     erInternalInfill,
24     erSolidInfill,
25     erTopSolidInfill,
26     erIroning,
27     erBridgeInfill,
28     erGapFill,
29     erSkirt,
30     erSupportMaterial,
31     erSupportMaterialInterface,
32     erWipeTower,
33     erCustom,
34     // Extrusion role for a collection with multiple extrusion roles.
35     erMixed,
36     erCount
37 };
38 
39 // Special flags describing loop
40 enum ExtrusionLoopRole {
41     elrDefault,
42     elrContourInternalPerimeter,
43     elrSkirt,
44 };
45 
46 
is_perimeter(ExtrusionRole role)47 inline bool is_perimeter(ExtrusionRole role)
48 {
49     return role == erPerimeter
50         || role == erExternalPerimeter
51         || role == erOverhangPerimeter;
52 }
53 
is_infill(ExtrusionRole role)54 inline bool is_infill(ExtrusionRole role)
55 {
56     return role == erBridgeInfill
57         || role == erInternalInfill
58         || role == erSolidInfill
59         || role == erTopSolidInfill
60         || role == erIroning;
61 }
62 
is_solid_infill(ExtrusionRole role)63 inline bool is_solid_infill(ExtrusionRole role)
64 {
65     return role == erBridgeInfill
66         || role == erSolidInfill
67         || role == erTopSolidInfill
68         || role == erIroning;
69 }
70 
is_bridge(ExtrusionRole role)71 inline bool is_bridge(ExtrusionRole role) {
72     return role == erBridgeInfill
73         || role == erOverhangPerimeter;
74 }
75 
76 class ExtrusionEntity
77 {
78 public:
79     virtual ExtrusionRole role() const = 0;
is_collection() const80     virtual bool is_collection() const { return false; }
is_loop() const81     virtual bool is_loop() const { return false; }
can_reverse() const82     virtual bool can_reverse() const { return true; }
83     virtual ExtrusionEntity* clone() const = 0;
84     // Create a new object, initialize it with this object using the move semantics.
85     virtual ExtrusionEntity* clone_move() = 0;
~ExtrusionEntity()86     virtual ~ExtrusionEntity() {}
87     virtual void reverse() = 0;
88     virtual const Point& first_point() const = 0;
89     virtual const Point& last_point() const = 0;
90     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
91     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
92     virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0;
93     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
94     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
95     // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
96     virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const = 0;
polygons_covered_by_width(const float scaled_epsilon=0.f) const97     Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
98         { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
polygons_covered_by_spacing(const float scaled_epsilon=0.f) const99     Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
100         { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
101     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
102     virtual double min_mm3_per_mm() const = 0;
103     virtual Polyline as_polyline() const = 0;
104     virtual void   collect_polylines(Polylines &dst) const = 0;
as_polylines() const105     virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
106     virtual double length() const = 0;
107     virtual double total_volume() const = 0;
108 
109     static std::string role_to_string(ExtrusionRole role);
110     static ExtrusionRole string_to_role(const std::string_view role);
111 };
112 
113 typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
114 
115 class ExtrusionPath : public ExtrusionEntity
116 {
117 public:
118     Polyline polyline;
119     // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
120     double mm3_per_mm;
121     // Width of the extrusion, used for visualization purposes.
122     float width;
123     // Height of the extrusion, used for visualization purposes.
124     float height;
125 
ExtrusionPath(ExtrusionRole role)126     ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}
ExtrusionPath(ExtrusionRole role,double mm3_per_mm,float width,float height)127     ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
ExtrusionPath(const ExtrusionPath & rhs)128     ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
ExtrusionPath(ExtrusionPath && rhs)129     ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
ExtrusionPath(const Polyline & polyline,const ExtrusionPath & rhs)130     ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
ExtrusionPath(Polyline && polyline,const ExtrusionPath & rhs)131     ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
132 
operator =(const ExtrusionPath & rhs)133     ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; }
operator =(ExtrusionPath && rhs)134     ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; }
135 
clone() const136 	ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); }
137     // Create a new object, initialize it with this object using the move semantics.
clone_move()138 	ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); }
reverse()139     void reverse() override { this->polyline.reverse(); }
first_point() const140     const Point& first_point() const override { return this->polyline.points.front(); }
last_point() const141     const Point& last_point() const override { return this->polyline.points.back(); }
size() const142     size_t size() const { return this->polyline.size(); }
empty() const143     bool empty() const { return this->polyline.empty(); }
is_closed() const144     bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
145     // Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
146     // Currently not used.
147     void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
148     // Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection.
149     // Currently not used.
150     void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
151     void clip_end(double distance);
152     void simplify(double tolerance);
153     double length() const override;
role() const154     ExtrusionRole role() const override { return m_role; }
155     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
156     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
157     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
158     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
159     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
160     // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
161     void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
polygons_covered_by_width(const float scaled_epsilon=0.f) const162     Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
163         { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
polygons_covered_by_spacing(const float scaled_epsilon=0.f) const164     Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
165         { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
166     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
min_mm3_per_mm() const167     double min_mm3_per_mm() const override { return this->mm3_per_mm; }
as_polyline() const168     Polyline as_polyline() const override { return this->polyline; }
collect_polylines(Polylines & dst) const169     void   collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
total_volume() const170     double total_volume() const override { return mm3_per_mm * unscale<double>(length()); }
171 
172 private:
173     void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
174 
175     ExtrusionRole m_role;
176 };
177 
178 typedef std::vector<ExtrusionPath> ExtrusionPaths;
179 
180 // Single continuous extrusion path, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
181 class ExtrusionMultiPath : public ExtrusionEntity
182 {
183 public:
184     ExtrusionPaths paths;
185 
ExtrusionMultiPath()186     ExtrusionMultiPath() {}
ExtrusionMultiPath(const ExtrusionMultiPath & rhs)187     ExtrusionMultiPath(const ExtrusionMultiPath &rhs) : paths(rhs.paths) {}
ExtrusionMultiPath(ExtrusionMultiPath && rhs)188     ExtrusionMultiPath(ExtrusionMultiPath &&rhs) : paths(std::move(rhs.paths)) {}
ExtrusionMultiPath(const ExtrusionPaths & paths)189     ExtrusionMultiPath(const ExtrusionPaths &paths) : paths(paths) {}
ExtrusionMultiPath(const ExtrusionPath & path)190     ExtrusionMultiPath(const ExtrusionPath &path) { this->paths.push_back(path); }
191 
operator =(const ExtrusionMultiPath & rhs)192     ExtrusionMultiPath& operator=(const ExtrusionMultiPath &rhs) { this->paths = rhs.paths; return *this; }
operator =(ExtrusionMultiPath && rhs)193     ExtrusionMultiPath& operator=(ExtrusionMultiPath &&rhs) { this->paths = std::move(rhs.paths); return *this; }
194 
is_loop() const195     bool is_loop() const override { return false; }
can_reverse() const196     bool can_reverse() const override { return true; }
clone() const197 	ExtrusionEntity* clone() const override { return new ExtrusionMultiPath(*this); }
198     // Create a new object, initialize it with this object using the move semantics.
clone_move()199 	ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); }
200     void reverse() override;
first_point() const201     const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
last_point() const202     const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
203     double length() const override;
role() const204     ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
205     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
206     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
207     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
208     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
209     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
210     // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
211     void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
polygons_covered_by_width(const float scaled_epsilon=0.f) const212     Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
213         { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
polygons_covered_by_spacing(const float scaled_epsilon=0.f) const214     Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
215         { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
216     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
217     double min_mm3_per_mm() const override;
218     Polyline as_polyline() const override;
collect_polylines(Polylines & dst) const219     void   collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
total_volume() const220     double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
221 };
222 
223 // Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
224 class ExtrusionLoop : public ExtrusionEntity
225 {
226 public:
227     ExtrusionPaths paths;
228 
ExtrusionLoop(ExtrusionLoopRole role=elrDefault)229     ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) {}
ExtrusionLoop(const ExtrusionPaths & paths,ExtrusionLoopRole role=elrDefault)230     ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {}
ExtrusionLoop(ExtrusionPaths && paths,ExtrusionLoopRole role=elrDefault)231     ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) {}
ExtrusionLoop(const ExtrusionPath & path,ExtrusionLoopRole role=elrDefault)232     ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role)
233         { this->paths.push_back(path); }
ExtrusionLoop(const ExtrusionPath && path,ExtrusionLoopRole role=elrDefault)234     ExtrusionLoop(const ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role)
235         { this->paths.emplace_back(std::move(path)); }
is_loop() const236     bool is_loop() const override{ return true; }
can_reverse() const237     bool can_reverse() const override { return false; }
clone() const238 	ExtrusionEntity* clone() const override{ return new ExtrusionLoop (*this); }
239     // Create a new object, initialize it with this object using the move semantics.
clone_move()240 	ExtrusionEntity* clone_move() override { return new ExtrusionLoop(std::move(*this)); }
241     bool make_clockwise();
242     bool make_counter_clockwise();
243     void reverse() override;
first_point() const244     const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
last_point() const245     const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); }
246     Polygon polygon() const;
247     double length() const override;
248     bool split_at_vertex(const Point &point);
249     void split_at(const Point &point, bool prefer_non_overhang);
250     void clip_end(double distance, ExtrusionPaths* paths) const;
251     // Test, whether the point is extruded by a bridging flow.
252     // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
253     bool has_overhang_point(const Point &point) const;
role() const254     ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
loop_role() const255     ExtrusionLoopRole loop_role() const { return m_loop_role; }
256     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
257     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
258     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
259     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
260     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
261     // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
262     void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const  override;
polygons_covered_by_width(const float scaled_epsilon=0.f) const263     Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
264         { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
polygons_covered_by_spacing(const float scaled_epsilon=0.f) const265     Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
266         { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
267     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
268     double min_mm3_per_mm() const override;
as_polyline() const269     Polyline as_polyline() const override { return this->polygon().split_at_first_point(); }
collect_polylines(Polylines & dst) const270     void   collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
total_volume() const271     double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
272 
273     //static inline std::string role_to_string(ExtrusionLoopRole role);
274 
275 #ifndef NDEBUG
validate() const276 	bool validate() const {
277 		assert(this->first_point() == this->paths.back().polyline.points.back());
278 		for (size_t i = 1; i < paths.size(); ++ i)
279 			assert(this->paths[i - 1].polyline.points.back() == this->paths[i].polyline.points.front());
280 		return true;
281 	}
282 #endif /* NDEBUG */
283 
284 private:
285     ExtrusionLoopRole m_loop_role;
286 };
287 
extrusion_paths_append(ExtrusionPaths & dst,Polylines & polylines,ExtrusionRole role,double mm3_per_mm,float width,float height)288 inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
289 {
290     dst.reserve(dst.size() + polylines.size());
291     for (Polyline &polyline : polylines)
292         if (polyline.is_valid()) {
293             dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
294             dst.back().polyline = polyline;
295         }
296 }
297 
extrusion_paths_append(ExtrusionPaths & dst,Polylines && polylines,ExtrusionRole role,double mm3_per_mm,float width,float height)298 inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
299 {
300     dst.reserve(dst.size() + polylines.size());
301     for (Polyline &polyline : polylines)
302         if (polyline.is_valid()) {
303             dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
304             dst.back().polyline = std::move(polyline);
305         }
306     polylines.clear();
307 }
308 
extrusion_entities_append_paths(ExtrusionEntitiesPtr & dst,Polylines & polylines,ExtrusionRole role,double mm3_per_mm,float width,float height)309 inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
310 {
311     dst.reserve(dst.size() + polylines.size());
312     for (Polyline &polyline : polylines)
313         if (polyline.is_valid()) {
314             ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
315             dst.push_back(extrusion_path);
316             extrusion_path->polyline = polyline;
317         }
318 }
319 
extrusion_entities_append_paths(ExtrusionEntitiesPtr & dst,Polylines && polylines,ExtrusionRole role,double mm3_per_mm,float width,float height)320 inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
321 {
322     dst.reserve(dst.size() + polylines.size());
323     for (Polyline &polyline : polylines)
324         if (polyline.is_valid()) {
325             ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
326             dst.push_back(extrusion_path);
327             extrusion_path->polyline = std::move(polyline);
328         }
329     polylines.clear();
330 }
331 
extrusion_entities_append_loops(ExtrusionEntitiesPtr & dst,Polygons && loops,ExtrusionRole role,double mm3_per_mm,float width,float height)332 inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, ExtrusionRole role, double mm3_per_mm, float width, float height)
333 {
334     dst.reserve(dst.size() + loops.size());
335     for (Polygon &poly : loops) {
336         if (poly.is_valid()) {
337             ExtrusionPath path(role, mm3_per_mm, width, height);
338             path.polyline.points = std::move(poly.points);
339             path.polyline.points.push_back(path.polyline.points.front());
340             dst.emplace_back(new ExtrusionLoop(std::move(path)));
341         }
342     }
343     loops.clear();
344 }
345 
346 }
347 
348 #endif
349