From e016c4e423a468b7351b7e03a49e42493bab0242 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 19 Jan 2017 13:35:55 +0100 Subject: [PATCH] New extrusion class: ExtrusionMultiPath This is similar to an ExtrusionLoop, but it is open. It may contain multiple chained paths with differing parameters. This allows one to have a hierarchy of paths, where the ExtrusionEntityCollection will be chained by the G-code generator, but ExtrusionMultiPath will not. --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI/Plater/2DToolpaths.pm | 4 +- xs/MANIFEST | 1 + xs/lib/Slic3r/XS.pm | 5 +++ xs/src/libslic3r/ExtrusionEntity.cpp | 56 ++++++++++++++++++++++++++++ xs/src/libslic3r/ExtrusionEntity.hpp | 49 ++++++++++++++++++++++++ xs/src/libslic3r/GCode.cpp | 22 +++++++++++ xs/src/libslic3r/GCode.hpp | 1 + xs/src/perlglue.cpp | 1 + xs/xsp/ExtrusionEntityCollection.xsp | 4 ++ xs/xsp/GCode.xsp | 2 + xs/xsp/my.map | 4 ++ xs/xsp/typemap.xspt | 3 ++ 13 files changed, 151 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 0d7d350d6..5f4357a95 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -223,6 +223,7 @@ sub thread_cleanup { *Slic3r::ExPolygon::Collection::DESTROY = sub {}; *Slic3r::Extruder::DESTROY = sub {}; *Slic3r::ExtrusionLoop::DESTROY = sub {}; + *Slic3r::ExtrusionMultiPath::DESTROY = sub {}; *Slic3r::ExtrusionPath::DESTROY = sub {}; *Slic3r::ExtrusionPath::Collection::DESTROY = sub {}; *Slic3r::ExtrusionSimulator::DESTROY = sub {}; diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm index d16395a55..5f7ddc86c 100644 --- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm +++ b/lib/Slic3r/GUI/Plater/2DToolpaths.pm @@ -488,7 +488,7 @@ sub Render { sub _draw { my ($self, $object, $print_z, $path) = @_; - my @paths = $path->isa('Slic3r::ExtrusionLoop') + my @paths = ($path->isa('Slic3r::ExtrusionLoop') || $path->isa('Slic3r::ExtrusionMultiPath')) ? @$path : ($path); @@ -546,7 +546,7 @@ sub _simulate_extrusion { push @extrusions, @$_ for @{$layerm->fills}; } foreach my $extrusion_entity (@extrusions) { - my @paths = $extrusion_entity->isa('Slic3r::ExtrusionLoop') + my @paths = ($extrusion_entity->isa('Slic3r::ExtrusionLoop') || $extrusion_entity->isa('Slic3r::ExtrusionMultiPath')) ? @$extrusion_entity : ($extrusion_entity); foreach my $path (@paths) { diff --git a/xs/MANIFEST b/xs/MANIFEST index 01b9e1cd3..db101d689 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -185,6 +185,7 @@ xsp/ExPolygonCollection.xsp xsp/Extruder.xsp xsp/ExtrusionEntityCollection.xsp xsp/ExtrusionLoop.xsp +xsp/ExtrusionMultiPath.xsp xsp/ExtrusionPath.xsp xsp/ExtrusionSimulator.xsp xsp/Filler.xsp diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index f99e9f65c..83766d4b9 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -107,6 +107,11 @@ sub new_from_paths { return $loop; } +package Slic3r::ExtrusionMultiPath; +use overload + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1; + package Slic3r::ExtrusionPath; use overload '@{}' => sub { $_[0]->arrayref }, diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 9f59ca386..d960e2838 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -63,6 +63,62 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } +void ExtrusionMultiPath::reverse() +{ + for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) + path->reverse(); + std::reverse(this->paths.begin(), this->paths.end()); +} + +double ExtrusionMultiPath::length() const +{ + double len = 0; + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + len += path->polyline.length(); + return len; +} + +void ExtrusionMultiPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const +{ + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + path->polygons_covered_by_width(out, scaled_epsilon); +} + +void ExtrusionMultiPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const +{ + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + path->polygons_covered_by_spacing(out, scaled_epsilon); +} + +double ExtrusionMultiPath::min_mm3_per_mm() const +{ + double min_mm3_per_mm = std::numeric_limits::max(); + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm); + return min_mm3_per_mm; +} + +Polyline ExtrusionMultiPath::as_polyline() const +{ + size_t len = 0; + for (size_t i_path = 0; i_path < paths.size(); ++ i_path) { + assert(! paths[i_path].polyline.points.empty()); + assert(i_path == 0 || paths[i_path - 1].polyline.points.back() == paths[i_path].polyline.points.front()); + len += paths[i_path].polyline.points.size(); + } + // The connecting points between the segments are equal. + len -= paths.size() - 1; + + Polyline out; + if (len > 0) { + out.points.reserve(len); + out.points.push_back(paths.front().polyline.points.front()); + for (size_t i_path = 0; i_path < paths.size(); ++ i_path) + out.points.insert(out.points.end(), paths[i_path].polyline.points.begin() + 1, paths[i_path].polyline.points.end()); + } + return out; +} + bool ExtrusionLoop::make_clockwise() { diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index dfc28d035..a04524767 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -133,6 +133,55 @@ public: typedef std::vector ExtrusionPaths; +// Single continuous extrusion path, possibly with varying extrusion thickness, extrusion height or bridging / non bridging. +class ExtrusionMultiPath : public ExtrusionEntity +{ +public: + ExtrusionPaths paths; + + ExtrusionMultiPath() {}; + ExtrusionMultiPath(const ExtrusionPaths &paths) : paths(paths) {}; + ExtrusionMultiPath(const ExtrusionPath &path) { this->paths.push_back(path); } + bool is_loop() const { return false; } + bool can_reverse() const { return true; } + ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); } + void reverse(); + Point first_point() const { return this->paths.front().polyline.points.front(); } + Point last_point() const { return this->paths.back().polyline.points.back(); } + virtual double length() const; + bool is_perimeter() const { + return this->paths.front().role == erPerimeter + || this->paths.front().role == erExternalPerimeter + || this->paths.front().role == erOverhangPerimeter; + } + bool is_infill() const { + return this->paths.front().role == erBridgeInfill + || this->paths.front().role == erInternalInfill + || this->paths.front().role == erSolidInfill + || this->paths.front().role == erTopSolidInfill; + } + bool is_solid_infill() const { + return this->paths.front().role == erBridgeInfill + || this->paths.front().role == erSolidInfill + || this->paths.front().role == erTopSolidInfill; + } + // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. + // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. + void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; + // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing. + // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. + // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill. + void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const; + Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const + { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; } + Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const + { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } + // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. + double min_mm3_per_mm() const; + Polyline as_polyline() const; +}; + +// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging. class ExtrusionLoop : public ExtrusionEntity { public: diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index e23e53465..b5714644b 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -766,11 +766,33 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) return gcode; } +std::string +GCode::extrude(ExtrusionMultiPath multipath, std::string description, double speed) +{ + // extrude along the path + std::string gcode; + for (ExtrusionPaths::const_iterator path = multipath.paths.begin(); path != multipath.paths.end(); ++path) +// description += ExtrusionLoopRole2String(loop.role); +// description += ExtrusionRole2String(path->role); + gcode += this->_extrude(*path, description, speed); + + // reset acceleration + gcode += this->writer.set_acceleration(this->config.default_acceleration.value); + +//FIXME perform wipe on multi paths? +// if (this->wipe.enable) +// this->wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path + + return gcode; +} + std::string GCode::extrude(const ExtrusionEntity &entity, std::string description, double speed) { if (const ExtrusionPath* path = dynamic_cast(&entity)) { return this->extrude(*path, description, speed); + } else if (const ExtrusionMultiPath* multipath = dynamic_cast(&entity)) { + return this->extrude(*multipath, description, speed); } else if (const ExtrusionLoop* loop = dynamic_cast(&entity)) { return this->extrude(*loop, description, speed); } else { diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index e1b6ad03c..51a77f41c 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -121,6 +121,7 @@ class GCode { std::string change_layer(const Layer &layer); std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1); std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1); + std::string extrude(ExtrusionMultiPath multipath, std::string description = "", double speed = -1); std::string extrude(const ExtrusionPath &path, std::string description = "", double speed = -1); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone); diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index cae956fe3..523d3fe06 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -6,6 +6,7 @@ namespace Slic3r { REGISTER_CLASS(ExPolygon, "ExPolygon"); REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection"); REGISTER_CLASS(Extruder, "Extruder"); +REGISTER_CLASS(ExtrusionMultiPath, "ExtrusionMultiPath"); REGISTER_CLASS(ExtrusionPath, "ExtrusionPath"); REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); // there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index 11c005b66..cd8700df7 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -53,6 +53,8 @@ ExtrusionEntityCollection::arrayref() // return our item by reference if (ExtrusionPath* path = dynamic_cast(*it)) { sv_setref_pv( sv, perl_class_name_ref(path), path ); + } else if (ExtrusionMultiPath* multipath = dynamic_cast(*it)) { + sv_setref_pv( sv, perl_class_name_ref(multipath), multipath ); } else if (ExtrusionLoop* loop = dynamic_cast(*it)) { sv_setref_pv( sv, perl_class_name_ref(loop), loop ); } else if (ExtrusionEntityCollection* collection = dynamic_cast(*it)) { @@ -77,6 +79,8 @@ ExtrusionEntityCollection::append(...) // append COPIES if (ExtrusionPath* path = dynamic_cast(entity)) { THIS->entities.push_back( new ExtrusionPath(*path) ); + } else if (ExtrusionMultiPath* multipath = dynamic_cast(entity)) { + THIS->entities.push_back( new ExtrusionMultiPath(*multipath) ); } else if (ExtrusionLoop* loop = dynamic_cast(entity)) { THIS->entities.push_back( new ExtrusionLoop(*loop) ); } else if(ExtrusionEntityCollection* collection = dynamic_cast(entity)) { diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 4ecee70a9..507132dcf 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -166,6 +166,8 @@ %code{% RETVAL = THIS->change_layer(*layer); %}; %name{extrude_loop} std::string extrude(ExtrusionLoop* loop, std::string description = "", double speed = -1) %code{% RETVAL = THIS->extrude(*loop, description, speed); %}; + %name{extrude_multipath} std::string extrude(ExtrusionMultiPath* multipath, std::string description = "", double speed = -1) + %code{% RETVAL = THIS->extrude(*multipath, description, speed); %}; %name{extrude_path} std::string extrude(ExtrusionPath* path, std::string description = "", double speed = -1) %code{% RETVAL = THIS->extrude(*path, description, speed); %}; std::string travel_to(Point* point, ExtrusionRole role, std::string comment) diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 0234bb3fa..c6308aca7 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -104,6 +104,10 @@ ExtrusionEntityCollection* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +ExtrusionMultiPath* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + ExtrusionPath* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index c9c3be5ae..e32271ac9 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -78,6 +78,9 @@ %typemap{ExtrusionEntityCollection*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{ExtrusionMultiPath*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{ExtrusionPath*}; %typemap{Ref}{simple}; %typemap{Clone}{simple};