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