diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 43999d365b..fa0edec2ba 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -90,7 +90,7 @@ struct stl_neighbors { struct stl_stats { stl_stats() { memset(&header, 0, 81); } - char header[81] = ""; + char header[81]; stl_type type = (stl_type)0; uint32_t number_of_facets = 0; stl_vertex max = stl_vertex::Zero(); diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 6fe68e56fd..5232be2b6f 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -100,7 +100,7 @@ add_library(libslic3r STATIC Geometry.cpp Geometry.hpp Int128.hpp - KdTreeIndirect.hpp + KDTreeIndirect.hpp Layer.cpp Layer.hpp LayerRegion.cpp @@ -131,8 +131,6 @@ add_library(libslic3r STATIC PolygonTrimmer.hpp Polyline.cpp Polyline.hpp - PolylineCollection.cpp - PolylineCollection.hpp Print.cpp Print.hpp PrintBase.cpp diff --git a/src/libslic3r/ExPolygonCollection.hpp b/src/libslic3r/ExPolygonCollection.hpp index 4c181cd6a0..099a6a1125 100644 --- a/src/libslic3r/ExPolygonCollection.hpp +++ b/src/libslic3r/ExPolygonCollection.hpp @@ -13,12 +13,12 @@ typedef std::vector ExPolygonCollections; class ExPolygonCollection { - public: +public: ExPolygons expolygons; - ExPolygonCollection() {}; + ExPolygonCollection() {} ExPolygonCollection(const ExPolygon &expolygon); - ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}; + ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {} operator Points() const; operator Polygons() const; operator ExPolygons&(); diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index dfc180689b..b22d85b657 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -14,7 +14,7 @@ class ExtrusionEntityCollection; class Extruder; // Each ExtrusionRole value identifies a distinct set of { extruder, speed } -enum ExtrusionRole { +enum ExtrusionRole : uint8_t { erNone, erPerimeter, erExternalPerimeter, @@ -81,8 +81,8 @@ public: virtual ExtrusionEntity* clone_move() = 0; virtual ~ExtrusionEntity() {} virtual void reverse() = 0; - virtual Point first_point() const = 0; - virtual Point last_point() const = 0; + virtual const Point& first_point() const = 0; + virtual const Point& last_point() const = 0; // 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. virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0; @@ -117,30 +117,23 @@ public: float width; // Height of the extrusion, used for visualization purposes. float height; - // Feedrate of the extrusion, used for visualization purposes. - float feedrate; - // Id of the extruder, used for visualization purposes. - unsigned int extruder_id; - // Id of the color, used for visualization purposes in the color printing case. - unsigned int cp_color_id; - ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} - ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} - ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} -// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {}; + 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) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}; + 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) : 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) : 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) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - 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->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; } - 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->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; } + 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; } + 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; } ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); } // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); } void reverse() override { this->polyline.reverse(); } - Point first_point() const override { return this->polyline.points.front(); } - Point last_point() const override { return this->polyline.points.back(); } + const Point& first_point() const override { return this->polyline.points.front(); } + const Point& last_point() const override { return this->polyline.points.back(); } size_t size() const { return this->polyline.size(); } bool empty() const { return this->polyline.empty(); } bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } @@ -200,8 +193,8 @@ public: // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); } void reverse() override; - Point first_point() const override { return this->paths.front().polyline.points.front(); } - Point last_point() const override { return this->paths.back().polyline.points.back(); } + const Point& first_point() const override { return this->paths.front().polyline.points.front(); } + const Point& last_point() const override { return this->paths.back().polyline.points.back(); } double length() const override; ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. @@ -243,8 +236,8 @@ public: bool make_clockwise(); bool make_counter_clockwise(); void reverse() override; - Point first_point() const override { return this->paths.front().polyline.points.front(); } - Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } + const Point& first_point() const override { return this->paths.front().polyline.points.front(); } + const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } Polygon polygon() const; double length() const override; bool split_at_vertex(const Point &point); diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index f7fab1ba1b..8c7e00e60b 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -1,4 +1,5 @@ #include "ExtrusionEntityCollection.hpp" +#include "ShortestPath.hpp" #include #include #include @@ -73,78 +74,31 @@ void ExtrusionEntityCollection::remove(size_t i) this->entities.erase(this->entities.begin() + i); } -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const +ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const { - ExtrusionEntityCollection coll; - this->chained_path(&coll, no_reverse, role); - return coll; -} - -void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const -{ - if (this->entities.empty()) return; - this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role); -} - -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const -{ - ExtrusionEntityCollection coll; - this->chained_path_from(start_near, &coll, no_reverse, role); - return coll; -} - -void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const -{ - if (this->no_sort) { - *retval = *this; - return; - } - - retval->entities.reserve(this->entities.size()); - - // if we're asked to return the original indices, build a map - std::map indices_map; - - ExtrusionEntitiesPtr my_paths; - for (ExtrusionEntity * const &entity_src : this->entities) { - if (role != erMixed) { - // The caller wants only paths with a specific extrusion role. - auto role2 = entity_src->role(); - if (role != role2) { - // This extrusion entity does not match the role asked. - assert(role2 != erMixed); - continue; - } - } - - ExtrusionEntity *entity = entity_src->clone(); - my_paths.push_back(entity); -// if (orig_indices != nullptr) -// indices_map[entity] = &entity_src - &this->entities.front(); - } - - Points endpoints; - for (const ExtrusionEntity *entity : my_paths) { - endpoints.push_back(entity->first_point()); - endpoints.push_back((no_reverse || ! entity->can_reverse()) ? - entity->first_point() : entity->last_point()); - } - - while (! my_paths.empty()) { - // find nearest point - int start_index = start_near.nearest_point_index(endpoints); - int path_index = start_index/2; - ExtrusionEntity* entity = my_paths.at(path_index); - // never reverse loops, since it's pointless for chained path and callers might depend on orientation - if (start_index % 2 && !no_reverse && entity->can_reverse()) - entity->reverse(); - retval->entities.push_back(my_paths.at(path_index)); -// if (orig_indices != nullptr) -// orig_indices->push_back(indices_map[entity]); - my_paths.erase(my_paths.begin() + path_index); - endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); - start_near = retval->entities.back()->last_point(); - } + ExtrusionEntityCollection out; + if (this->no_sort) { + out = *this; + } else { + if (role == erMixed) + out = *this; + else { + for (const ExtrusionEntity *ee : this->entities) { + if (role != erMixed) { + // The caller wants only paths with a specific extrusion role. + auto role2 = ee->role(); + if (role != role2) { + // This extrusion entity does not match the role asked. + assert(role2 != erMixed); + continue; + } + } + out.entities.emplace_back(ee->clone()); + } + } + chain_and_reorder_extrusion_entities(out.entities, &start_near); + } + return out; } void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index e92fa156f5..4e14917240 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -65,13 +65,10 @@ public: } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const; - void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const; - ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const; - void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const; + ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const; void reverse(); - Point first_point() const { return this->entities.front()->first_point(); } - Point last_point() const { return this->entities.back()->last_point(); } + const Point& first_point() const { return this->entities.front()->first_point(); } + const Point& last_point() const { return this->entities.back()->last_point(); } // 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 override; diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 44267d3a47..820f0008bf 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -176,7 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single( } } bool first = true; - for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { + for (Polyline &polyline : chain_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 09f0782bc9..7cd9558923 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -167,7 +167,7 @@ void FillGyroid::_fill_surface_single( } } bool first = true; - for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { + for (Polyline &polyline : chain_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; diff --git a/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp index be569c2d84..948af182bb 100644 --- a/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/src/libslic3r/Fill/FillHoneycomb.cpp @@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single( // connect paths if (! paths.empty()) { // prevent calling leftmost_point() on empty collections - Polylines chained = chain_infill_polylines(std::move(paths)); + Polylines chained = chain_polylines(std::move(paths)); assert(paths.empty()); paths.clear(); for (Polyline &path : chained) { diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index 3b9266a0fe..7a322ce991 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -1,5 +1,4 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" #include "../Surface.hpp" #include "FillPlanePath.hpp" diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index d535ba29b5..629e5b6f46 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -93,7 +93,7 @@ void FillRectilinear::_fill_surface_single( } } bool first = true; - for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { + for (Polyline &polyline : chain_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 348834b3c4..65264c9cd8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -117,11 +117,11 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP const Layer* layer1 = object->layers()[i * 2]; const Layer* layer2 = object->layers()[i * 2 + 1]; Polygons polys; - polys.reserve(layer1->slices.expolygons.size() + layer2->slices.expolygons.size()); - for (const ExPolygon &expoly : layer1->slices.expolygons) + polys.reserve(layer1->slices.size() + layer2->slices.size()); + for (const ExPolygon &expoly : layer1->slices) //FIXME no holes? polys.emplace_back(expoly.contour); - for (const ExPolygon &expoly : layer2->slices.expolygons) + for (const ExPolygon &expoly : layer2->slices) //FIXME no holes? polys.emplace_back(expoly.contour); polygons_per_layer[i] = union_(polys); @@ -130,8 +130,8 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP if (object->layers().size() & 1) { const Layer *layer = object->layers().back(); Polygons polys; - polys.reserve(layer->slices.expolygons.size()); - for (const ExPolygon &expoly : layer->slices.expolygons) + polys.reserve(layer->slices.size()); + for (const ExPolygon &expoly : layer->slices) //FIXME no holes? polys.emplace_back(expoly.contour); polygons_per_layer.back() = union_(polys); @@ -1802,11 +1802,8 @@ void GCode::process_layer( // - for each island, we extrude perimeters first, unless user set the infill_first // option // (Still, we have to keep track of regions because we need to apply their config) - size_t n_slices = layer.slices.expolygons.size(); - std::vector layer_surface_bboxes; - layer_surface_bboxes.reserve(n_slices); - for (const ExPolygon &expoly : layer.slices.expolygons) - layer_surface_bboxes.push_back(get_extents(expoly.contour)); + size_t n_slices = layer.slices.size(); + const std::vector &layer_surface_bboxes = layer.slices_bboxes; // Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first, // so we can just test a point inside ExPolygon::contour and we may skip testing the holes. std::vector slices_test_order; @@ -1822,7 +1819,7 @@ void GCode::process_layer( const BoundingBox &bbox = layer_surface_bboxes[i]; return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && point(1) >= bbox.min(1) && point(1) < bbox.max(1) && - layer.slices.expolygons[i].contour.contains(point); + layer.slices[i].contour.contains(point); }; for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { @@ -1969,7 +1966,7 @@ void GCode::process_layer( m_layer = layers[instance_to_print.layer_id].support_layer; gcode += this->extrude_support( // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. - instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, false, instance_to_print.object_by_extruder.support_extrusion_role)); + instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role)); m_layer = layers[instance_to_print.layer_id].layer(); } for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { @@ -2418,7 +2415,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou static int iRun = 0; SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++)); if (m_layer->lower_layer != NULL) - svg.draw(m_layer->lower_layer->slices.expolygons); + svg.draw(m_layer->lower_layer->slices); for (size_t i = 0; i < loop.paths.size(); ++ i) svg.draw(loop.paths[i].as_polyline(), "red"); Polylines polylines; @@ -2588,10 +2585,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig()); - for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) { + for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos).entities) { auto *eec = dynamic_cast(fill); if (eec) { - for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities) + for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) gcode += this->extrude_entity(*ee, "infill"); } else gcode += this->extrude_entity(*fill, "infill"); diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 20f0483b0e..136959d712 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -20,6 +20,7 @@ static const unsigned int DEFAULT_EXTRUDER_ID = 0; static const unsigned int DEFAULT_COLOR_PRINT_ID = 0; static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f); static const float DEFAULT_START_EXTRUSION = 0.0f; +static const float DEFAULT_FAN_SPEED = 0.0f; namespace Slic3r { @@ -36,21 +37,23 @@ const float GCodeAnalyzer::Default_Height = 0.0f; GCodeAnalyzer::Metadata::Metadata() : extrusion_role(erNone) , extruder_id(DEFAULT_EXTRUDER_ID) - , cp_color_id(DEFAULT_COLOR_PRINT_ID) , mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm) , width(GCodeAnalyzer::Default_Width) , height(GCodeAnalyzer::Default_Height) , feedrate(DEFAULT_FEEDRATE) + , fan_speed(DEFAULT_FAN_SPEED) + , cp_color_id(DEFAULT_COLOR_PRINT_ID) { } -GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id/* = 0*/) +GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id/* = 0*/) : extrusion_role(extrusion_role) , extruder_id(extruder_id) , mm3_per_mm(mm3_per_mm) , width(width) , height(height) , feedrate(feedrate) + , fan_speed(fan_speed) , cp_color_id(cp_color_id) { } @@ -75,15 +78,18 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other) if (feedrate != other.feedrate) return true; + if (fan_speed != other.fan_speed) + return true; + if (cp_color_id != other.cp_color_id) return true; return false; } -GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id/* = 0*/) +GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id/* = 0*/) : type(type) - , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, cp_color_id) + , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, fan_speed, cp_color_id) , start_position(start_position) , end_position(end_position) , delta_extruder(delta_extruder) @@ -133,6 +139,7 @@ void GCodeAnalyzer::reset() _set_feedrate(DEFAULT_FEEDRATE); _set_start_position(DEFAULT_START_POSITION); _set_start_extrusion(DEFAULT_START_EXTRUSION); + _set_fan_speed(DEFAULT_FAN_SPEED); _reset_axes_position(); _reset_cached_position(); @@ -259,6 +266,16 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi _processM83(line); break; } + case 106: // Set fan speed + { + _processM106(line); + break; + } + case 107: // Disable fan + { + _processM107(line); + break; + } case 108: case 135: { @@ -448,6 +465,24 @@ void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line) _set_e_local_positioning_type(Relative); } +void GCodeAnalyzer::_processM106(const GCodeReader::GCodeLine& line) +{ + if (!line.has('P')) + { + // The absence of P means the print cooling fan, so ignore anything else. + float new_fan_speed; + if (line.has_value('S', new_fan_speed)) + _set_fan_speed((100.0f / 256.0f) * new_fan_speed); + else + _set_fan_speed(100.0f); + } +} + +void GCodeAnalyzer::_processM107(const GCodeReader::GCodeLine& line) +{ + _set_fan_speed(0.0f); +} + void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line) { // These M-codes are used by MakerWare and Sailfish to change active tool. @@ -726,6 +761,16 @@ float GCodeAnalyzer::_get_feedrate() const return m_state.data.feedrate; } +void GCodeAnalyzer::_set_fan_speed(float fan_speed_percentage) +{ + m_state.data.fan_speed = fan_speed_percentage; +} + +float GCodeAnalyzer::_get_fan_speed() const +{ + return m_state.data.fan_speed; +} + void GCodeAnalyzer::_set_axis_position(EAxis axis, float position) { m_state.position[axis] = position; @@ -798,7 +843,7 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) Vec3d start_position = _get_start_position() + extruder_offset; Vec3d end_position = _get_end_position() + extruder_offset; - it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id()); + it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); } bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const @@ -821,7 +866,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ } // if layer not found, create and return it - layers.emplace_back(z, ExtrusionPaths()); + layers.emplace_back(z, GCodePreviewData::Extrusion::Paths()); return layers.back(); } @@ -830,13 +875,18 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ // if the polyline is valid, create the extrusion path from it and store it if (polyline.is_valid()) { - ExtrusionPath path(data.extrusion_role, data.mm3_per_mm, data.width, data.height); + auto& paths = get_layer_at_z(preview_data.extrusion.layers, z).paths; + paths.emplace_back(GCodePreviewData::Extrusion::Path()); + GCodePreviewData::Extrusion::Path &path = paths.back(); path.polyline = polyline; + path.extrusion_role = data.extrusion_role; + path.mm3_per_mm = data.mm3_per_mm; + path.width = data.width; + path.height = data.height; path.feedrate = data.feedrate; path.extruder_id = data.extruder_id; path.cp_color_id = data.cp_color_id; - - get_layer_at_z(preview_data.extrusion.layers, z).paths.push_back(path); + path.fan_speed = data.fan_speed; } } }; @@ -854,6 +904,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ GCodePreviewData::Range width_range; GCodePreviewData::Range feedrate_range; GCodePreviewData::Range volumetric_rate_range; + GCodePreviewData::Range fan_speed_range; // to avoid to call the callback too often unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1); @@ -888,6 +939,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ width_range.update_from(move.data.width); feedrate_range.update_from(move.data.feedrate); volumetric_rate_range.update_from(volumetric_rate); + fan_speed_range.update_from(move.data.fan_speed); } else // append end vertex of the move to current polyline @@ -906,6 +958,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ preview_data.ranges.width.update_from(width_range); preview_data.ranges.feedrate.update_from(feedrate_range); preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range); + preview_data.ranges.fan_speed.update_from(fan_speed_range); // we need to sort the layers by their z as they can be shuffled in case of sequential prints std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; }); diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index 0372d9da72..529610b0b2 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -54,10 +54,11 @@ public: float width; // mm float height; // mm float feedrate; // mm/s + float fan_speed; // percentage unsigned int cp_color_id; Metadata(); - Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id = 0); + Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id = 0); bool operator != (const Metadata& other) const; }; @@ -81,7 +82,7 @@ public: Vec3d end_position; float delta_extruder; - GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id = 0); + GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id = 0); GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder); }; @@ -171,6 +172,12 @@ private: // Set extruder to relative mode void _processM83(const GCodeReader::GCodeLine& line); + // Set fan speed + void _processM106(const GCodeReader::GCodeLine& line); + + // Disable fan + void _processM107(const GCodeReader::GCodeLine& line); + // Set tool (MakerWare and Sailfish flavor) void _processM108orM135(const GCodeReader::GCodeLine& line); @@ -233,6 +240,9 @@ private: void _set_feedrate(float feedrate_mm_sec); float _get_feedrate() const; + void _set_fan_speed(float fan_speed_percentage); + float _get_fan_speed() const; + void _set_axis_position(EAxis axis, float position); float _get_axis_position(EAxis axis) const; diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 76f21daeb5..53c13a2f23 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -23,7 +23,7 @@ std::vector GCodePreviewData::Color::as_bytes() const return ret; } -GCodePreviewData::Extrusion::Layer::Layer(float z, const ExtrusionPaths& paths) +GCodePreviewData::Extrusion::Layer::Layer(float z, const Paths& paths) : z(z) , paths(paths) { @@ -171,8 +171,8 @@ size_t GCodePreviewData::Extrusion::memory_used() const size_t out = sizeof(*this); out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer); for (const Layer &layer : this->layers) { - out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath); - for (const ExtrusionPath &path : layer.paths) + out += SLIC3R_STDVEC_MEMSIZE(layer.paths, Path); + for (const Path &path : layer.paths) out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point); } return out; @@ -241,6 +241,7 @@ void GCodePreviewData::set_default() ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); extrusion.set_default(); @@ -287,6 +288,11 @@ GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) con return ranges.feedrate.get_color_at(feedrate); } +GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const +{ + return ranges.fan_speed.get_color_at(fan_speed); +} + GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const { return ranges.volumetric_rate.get_color_at(rate); @@ -358,6 +364,8 @@ std::string GCodePreviewData::get_legend_title() const return L("Width (mm)"); case Extrusion::Feedrate: return L("Speed (mm/s)"); + case Extrusion::FanSpeed: + return L("Fan Speed (%)"); case Extrusion::VolumetricRate: return L("Volumetric flow rate (mm³/s)"); case Extrusion::Tool: @@ -421,6 +429,11 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f); break; } + case Extrusion::FanSpeed: + { + Helper::FillListFromRange(items, ranges.fan_speed, 0, 1.0f); + break; + } case Extrusion::VolumetricRate: { Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f); diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 6490399b42..70b6edffdf 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -52,6 +52,8 @@ public: Range width; // Color mapping by feedrate. Range feedrate; + // Color mapping by fan speed. + Range fan_speed; // Color mapping by volumetric extrusion rate. Range volumetric_rate; }; @@ -74,6 +76,7 @@ public: Height, Width, Feedrate, + FanSpeed, VolumetricRate, Tool, ColorPrint, @@ -84,12 +87,34 @@ public: static const std::string Default_Extrusion_Role_Names[erCount]; static const EViewType Default_View_Type; + class Path + { + public: + Polyline polyline; + ExtrusionRole extrusion_role; + // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. + float mm3_per_mm; + // Width of the extrusion, used for visualization purposes. + float width; + // Height of the extrusion, used for visualization purposes. + float height; + // Feedrate of the extrusion, used for visualization purposes. + float feedrate; + // Id of the extruder, used for visualization purposes. + uint32_t extruder_id; + // Id of the color, used for visualization purposes in the color printing case. + uint32_t cp_color_id; + // Fan speed for the extrusion, used for visualization purposes. + float fan_speed; + }; + using Paths = std::vector; + struct Layer { float z; - ExtrusionPaths paths; + Paths paths; - Layer(float z, const ExtrusionPaths& paths); + Layer(float z, const Paths& paths); }; typedef std::vector LayersList; @@ -205,6 +230,7 @@ public: Color get_height_color(float height) const; Color get_width_color(float width) const; Color get_feedrate_color(float feedrate) const; + Color get_fan_speed_color(float fan_speed) const; Color get_volumetric_rate_color(float rate) const; void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha); diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 8cf4047a1a..e80b365bb5 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -3,7 +3,6 @@ #include "ClipperUtils.hpp" #include "ExPolygon.hpp" #include "Line.hpp" -#include "PolylineCollection.hpp" #include "clipper.hpp" #include #include diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp index f79dab9b36..3cccfdafac 100644 --- a/src/libslic3r/KDTreeIndirect.hpp +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -19,7 +19,10 @@ public: static constexpr size_t NumDimensions = ANumDimensions; using CoordinateFn = ACoordinateFn; using CoordType = ACoordType; - static constexpr size_t npos = size_t(-1); + // Following could be static constexpr size_t, but that would not link in C++11 + enum : size_t { + npos = size_t(-1) + }; KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {} KDTreeIndirect(CoordinateFn coordinate, std::vector indices) : coordinate(coordinate) { this->build(std::move(indices)); } @@ -61,15 +64,17 @@ public: { CoordType dist = point_coord - this->coordinate(idx, dimension); return (dist * dist < search_radius + CoordType(EPSILON)) ? + // The plane intersects a hypersphere centered at point_coord of search_radius. ((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) : - (dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); + // The plane does not intersect the hypersphere. + (dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); } // Visitor is supposed to return a bit mask of VisitorReturnMask. template void visit(Visitor &visitor) const { - return m_nodes.empty() ? npos : visit_recursive(0, 0, visitor); + visit_recursive(0, 0, visitor); } CoordinateFn coordinate; diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 94f114a261..74deabf3e7 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -47,8 +47,8 @@ void Layer::make_slices() slices = union_ex(slices_p); } - this->slices.expolygons.clear(); - this->slices.expolygons.reserve(slices.size()); + this->slices.clear(); + this->slices.reserve(slices.size()); // prepare ordering points Points ordering_points; @@ -61,7 +61,7 @@ void Layer::make_slices() // populate slices vector for (size_t i : order) - this->slices.expolygons.push_back(std::move(slices[i])); + this->slices.push_back(std::move(slices[i])); } // Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill. @@ -70,7 +70,7 @@ void Layer::merge_slices() if (m_regions.size() == 1) { // Optimization, also more robust. Don't merge classified pieces of layerm->slices, // but use the non-split islands of a layer. For a single region print, these shall be equal. - m_regions.front()->slices.set(this->slices.expolygons, stInternal); + m_regions.front()->slices.set(this->slices, stInternal); } else { for (LayerRegion *layerm : m_regions) // without safety offset, artifacts are generated (GH #2494) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 555017207a..9a4297ce55 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -6,8 +6,6 @@ #include "SurfaceCollection.hpp" #include "ExtrusionEntityCollection.hpp" #include "ExPolygonCollection.hpp" -#include "PolylineCollection.hpp" - namespace Slic3r { @@ -48,7 +46,7 @@ public: Polygons bridged; // collection of polylines representing the unsupported bridge edges - PolylineCollection unsupported_bridge_edges; + Polylines unsupported_bridge_edges; // ordered collection of extrusion paths/loops to build all perimeters // (this collection contains only ExtrusionEntityCollection objects) @@ -112,7 +110,8 @@ public: // also known as 'islands' (all regions and surface types are merged here) // The slices are chained by the shortest traverse distance and this traversal // order will be recovered by the G-code generator. - ExPolygonCollection slices; + ExPolygons slices; + std::vector slices_bboxes; size_t region_count() const { return m_regions.size(); } const LayerRegion* get_region(int idx) const { return m_regions.at(idx); } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index bfe96d311c..0ff59d35fc 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -140,7 +140,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly // Remove voids from fill_boundaries, that are not supported by the layer below. if (lower_layer_covered == nullptr) { lower_layer_covered = &lower_layer_covered_tmp; - lower_layer_covered_tmp = to_polygons(lower_layer->slices.expolygons); + lower_layer_covered_tmp = to_polygons(lower_layer->slices); } if (! lower_layer_covered->empty()) voids = diff(voids, *lower_layer_covered); @@ -272,7 +272,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly bridges[idx_last].bridge_angle = bd.angle; if (this->layer()->object()->config().support_material) { polygons_append(this->bridged, bd.coverage()); - this->unsupported_bridge_edges.append(bd.unsupported_edges()); + append(this->unsupported_bridge_edges, bd.unsupported_edges()); } } else if (custom_angle > 0) { // Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index ee3b997476..39b07e7d86 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -3,11 +3,6 @@ namespace Slic3r { -MultiPoint::operator Points() const -{ - return this->points; -} - void MultiPoint::scale(double factor) { for (Point &pt : points) @@ -57,18 +52,7 @@ void MultiPoint::rotate(double angle, const Point ¢er) } } -void MultiPoint::reverse() -{ - std::reverse(this->points.begin(), this->points.end()); -} - -Point MultiPoint::first_point() const -{ - return this->points.front(); -} - -double -MultiPoint::length() const +double MultiPoint::length() const { Lines lines = this->lines(); double len = 0; @@ -78,8 +62,7 @@ MultiPoint::length() const return len; } -int -MultiPoint::find_point(const Point &point) const +int MultiPoint::find_point(const Point &point) const { for (const Point &pt : this->points) if (pt == point) @@ -87,21 +70,18 @@ MultiPoint::find_point(const Point &point) const return -1; // not found } -bool -MultiPoint::has_boundary_point(const Point &point) const +bool MultiPoint::has_boundary_point(const Point &point) const { double dist = (point.projection_onto(*this) - point).cast().norm(); return dist < SCALED_EPSILON; } -BoundingBox -MultiPoint::bounding_box() const +BoundingBox MultiPoint::bounding_box() const { return BoundingBox(this->points); } -bool -MultiPoint::has_duplicate_points() const +bool MultiPoint::has_duplicate_points() const { for (size_t i = 1; i < points.size(); ++i) if (points[i-1] == points[i]) @@ -109,8 +89,7 @@ MultiPoint::has_duplicate_points() const return false; } -bool -MultiPoint::remove_duplicate_points() +bool MultiPoint::remove_duplicate_points() { size_t j = 0; for (size_t i = 1; i < points.size(); ++i) { @@ -129,8 +108,7 @@ MultiPoint::remove_duplicate_points() return false; } -bool -MultiPoint::intersection(const Line& line, Point* intersection) const +bool MultiPoint::intersection(const Line& line, Point* intersection) const { Lines lines = this->lines(); for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 38020d6e84..9ff91b5022 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -17,7 +17,8 @@ class MultiPoint public: Points points; - operator Points() const; + operator Points() const { return this->points; } + MultiPoint() {} MultiPoint(const MultiPoint &other) : points(other.points) {} MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} @@ -32,9 +33,10 @@ public: void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } void rotate(double cos_angle, double sin_angle); void rotate(double angle, const Point ¢er); - void reverse(); - Point first_point() const; - virtual Point last_point() const = 0; + void reverse() { std::reverse(this->points.begin(), this->points.end()); } + + const Point& first_point() const { return this->points.front(); } + virtual const Point& last_point() const = 0; virtual Lines lines() const = 0; size_t size() const { return points.size(); } bool empty() const { return points.empty(); } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index cd53547769..450fff3515 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -175,10 +175,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime perimeter_generator.overhang_flow.width, perimeter_generator.overhang_flow.height); - // reapply the nearest point search for starting point - // We allow polyline reversal because Clipper may have randomly - // reversed polylines during clipping. - paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path(); + // Reapply the nearest point search for starting point. + // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. + chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); } else { ExtrusionPath path(role); path.polyline = loop.polygon.split_at_first_point(); diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 8cd71e6974..c0d9e65a78 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -3,7 +3,6 @@ #include "libslic3r.h" #include -#include "ExPolygonCollection.hpp" #include "Flow.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" @@ -15,7 +14,7 @@ class PerimeterGenerator { public: // Inputs: const SurfaceCollection *slices; - const ExPolygonCollection *lower_slices; + const ExPolygons *lower_slices; double layer_height; int layer_id; Flow perimeter_flow; @@ -45,7 +44,7 @@ public: ExtrusionEntityCollection* gap_fill, // Infills without the gap fills SurfaceCollection* fill_surfaces) - : slices(slices), lower_slices(NULL), layer_height(layer_height), + : slices(slices), lower_slices(nullptr), layer_height(layer_height), layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), solid_infill_flow(flow), config(config), object_config(object_config), print_config(print_config), diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index cf0783bae5..5ef5d0ceb1 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -5,43 +5,12 @@ namespace Slic3r { -Polygon::operator Polygons() const -{ - Polygons pp; - pp.push_back(*this); - return pp; -} - -Polygon::operator Polyline() const -{ - return this->split_at_first_point(); -} - -Point& -Polygon::operator[](Points::size_type idx) -{ - return this->points[idx]; -} - -const Point& -Polygon::operator[](Points::size_type idx) const -{ - return this->points[idx]; -} - -Point -Polygon::last_point() const -{ - return this->points.front(); // last point == first point for polygons -} - Lines Polygon::lines() const { return to_lines(*this); } -Polyline -Polygon::split_at_vertex(const Point &point) const +Polyline Polygon::split_at_vertex(const Point &point) const { // find index of point for (const Point &pt : this->points) @@ -52,8 +21,7 @@ Polygon::split_at_vertex(const Point &point) const } // Split a closed polygon into an open polyline, with the split point duplicated at both ends. -Polyline -Polygon::split_at_index(int index) const +Polyline Polygon::split_at_index(int index) const { Polyline polyline; polyline.points.reserve(this->points.size() + 1); @@ -64,19 +32,6 @@ Polygon::split_at_index(int index) const return polyline; } -// Split a closed polygon into an open polyline, with the split point duplicated at both ends. -Polyline -Polygon::split_at_first_point() const -{ - return this->split_at_index(0); -} - -Points -Polygon::equally_spaced_points(double distance) const -{ - return this->split_at_first_point().equally_spaced_points(distance); -} - /* int64_t Polygon::area2x() const { @@ -107,20 +62,17 @@ double Polygon::area() const return 0.5 * a; } -bool -Polygon::is_counter_clockwise() const +bool Polygon::is_counter_clockwise() const { return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); } -bool -Polygon::is_clockwise() const +bool Polygon::is_clockwise() const { return !this->is_counter_clockwise(); } -bool -Polygon::make_counter_clockwise() +bool Polygon::make_counter_clockwise() { if (!this->is_counter_clockwise()) { this->reverse(); @@ -129,8 +81,7 @@ Polygon::make_counter_clockwise() return false; } -bool -Polygon::make_clockwise() +bool Polygon::make_clockwise() { if (this->is_counter_clockwise()) { this->reverse(); @@ -139,16 +90,9 @@ Polygon::make_clockwise() return false; } -bool -Polygon::is_valid() const -{ - return this->points.size() >= 3; -} - // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. -bool -Polygon::contains(const Point &point) const +bool Polygon::contains(const Point &point) const { // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html bool result = false; @@ -174,8 +118,7 @@ Polygon::contains(const Point &point) const } // this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons() -Polygons -Polygon::simplify(double tolerance) const +Polygons Polygon::simplify(double tolerance) const { // repeat first point at the end in order to apply Douglas-Peucker // on the whole polygon @@ -189,8 +132,7 @@ Polygon::simplify(double tolerance) const return simplify_polygons(pp); } -void -Polygon::simplify(double tolerance, Polygons &polygons) const +void Polygon::simplify(double tolerance, Polygons &polygons) const { Polygons pp = this->simplify(tolerance); polygons.reserve(polygons.size() + pp.size()); @@ -198,8 +140,7 @@ Polygon::simplify(double tolerance, Polygons &polygons) const } // Only call this on convex polygons or it will return invalid results -void -Polygon::triangulate_convex(Polygons* polygons) const +void Polygon::triangulate_convex(Polygons* polygons) const { for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) { Polygon p; @@ -214,8 +155,7 @@ Polygon::triangulate_convex(Polygons* polygons) const } // center of mass -Point -Polygon::centroid() const +Point Polygon::centroid() const { double area_temp = this->area(); double x_temp = 0; @@ -232,8 +172,7 @@ Polygon::centroid() const // find all concave vertices (i.e. having an internal angle greater than the supplied angle) // (external = right side, thus we consider ccw orientation) -Points -Polygon::concave_points(double angle) const +Points Polygon::concave_points(double angle) const { Points points; angle = 2*PI - angle; @@ -256,8 +195,7 @@ Polygon::concave_points(double angle) const // find all convex vertices (i.e. having an internal angle smaller than the supplied angle) // (external = right side, thus we consider ccw orientation) -Points -Polygon::convex_points(double angle) const +Points Polygon::convex_points(double angle) const { Points points; angle = 2*PI - angle; diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 63162d9539..42f938c726 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -13,13 +13,14 @@ namespace Slic3r { class Polygon; typedef std::vector Polygons; -class Polygon : public MultiPoint { +class Polygon : public MultiPoint +{ public: - operator Polygons() const; - operator Polyline() const; - Point& operator[](Points::size_type idx); - const Point& operator[](Points::size_type idx) const; - + operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; } + operator Polyline() const { return this->split_at_first_point(); } + Point& operator[](Points::size_type idx) { return this->points[idx]; } + const Point& operator[](Points::size_type idx) const { return this->points[idx]; } + Polygon() {} explicit Polygon(const Points &points): MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} @@ -34,20 +35,24 @@ public: Polygon& operator=(const Polygon &other) { points = other.points; return *this; } Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } - Point last_point() const; + // last point == first point for polygons + const Point& last_point() const override { return this->points.front(); } + virtual Lines lines() const; Polyline split_at_vertex(const Point &point) const; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_index(int index) const; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. - Polyline split_at_first_point() const; - Points equally_spaced_points(double distance) const; + Polyline split_at_first_point() const { return this->split_at_index(0); } + Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); } + double area() const; bool is_counter_clockwise() const; bool is_clockwise() const; bool make_counter_clockwise(); bool make_clockwise(); - bool is_valid() const; + bool is_valid() const { return this->points.size() >= 3; } + // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. bool contains(const Point &point) const; diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 531383ae14..26aad83d2b 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -23,18 +23,17 @@ Polyline::operator Line() const return Line(this->points.front(), this->points.back()); } -Point -Polyline::leftmost_point() const +const Point& Polyline::leftmost_point() const { - Point p = this->points.front(); - for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { - if ((*it)(0) < p(0)) p = *it; + const Point *p = &this->points.front(); + for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++ it) { + if (it->x() < p->x()) + p = &(*it); } - return p; + return *p; } -Lines -Polyline::lines() const +Lines Polyline::lines() const { Lines lines; if (this->points.size() >= 2) { @@ -205,6 +204,20 @@ BoundingBox get_extents(const Polylines &polylines) return bb; } +const Point& leftmost_point(const Polylines &polylines) +{ + if (polylines.empty()) + throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); + Polylines::const_iterator it = polylines.begin(); + const Point *p = &it->leftmost_point(); + for (++ it; it != polylines.end(); ++it) { + const Point *p2 = &it->leftmost_point(); + if (p2->x() < p->x()) + p = p2; + } + return *p; +} + bool remove_degenerate(Polylines &polylines) { bool modified = false; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index d595f252d6..7e3a1e5065 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -62,9 +62,9 @@ public: operator Polylines() const; operator Line() const; - Point last_point() const override { return this->points.back(); } + const Point& last_point() const override { return this->points.back(); } - Point leftmost_point() const; + const Point& leftmost_point() const; virtual Lines lines() const; void clip_end(double distance); void clip_start(double distance); @@ -77,6 +77,15 @@ public: bool is_straight() const; }; +// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests! +#ifdef PERL_UCHAR_MIN +class PolylineCollection +{ +public: + Polylines polylines; +}; +#endif /* PERL_UCHAR_MIN */ + extern BoundingBox get_extents(const Polyline &polyline); extern BoundingBox get_extents(const Polylines &polylines); @@ -129,6 +138,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src) } } +const Point& leftmost_point(const Polylines &polylines); + bool remove_degenerate(Polylines &polylines); class ThickPolyline : public Polyline { diff --git a/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp deleted file mode 100644 index 0c66c371a9..0000000000 --- a/src/libslic3r/PolylineCollection.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "PolylineCollection.hpp" - -namespace Slic3r { - -struct Chaining -{ - Point first; - Point last; - size_t idx; -}; - -template -inline int nearest_point_index(const std::vector &pairs, const Point &start_near, bool no_reverse) -{ - T dmin = std::numeric_limits::max(); - int idx = 0; - for (std::vector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { - T d = sqr(T(start_near(0) - it->first(0))); - if (d <= dmin) { - d += sqr(T(start_near(1) - it->first(1))); - if (d < dmin) { - idx = (it - pairs.begin()) * 2; - dmin = d; - if (dmin < EPSILON) - break; - } - } - if (! no_reverse) { - d = sqr(T(start_near(0) - it->last(0))); - if (d <= dmin) { - d += sqr(T(start_near(1) - it->last(1))); - if (d < dmin) { - idx = (it - pairs.begin()) * 2 + 1; - dmin = d; - if (dmin < EPSILON) - break; - } - } - } - } - return idx; -} - -Polylines PolylineCollection::_chained_path_from( - const Polylines &src, - Point start_near, - bool no_reverse, - bool move_from_src) -{ - std::vector endpoints; - endpoints.reserve(src.size()); - for (size_t i = 0; i < src.size(); ++ i) { - Chaining c; - c.first = src[i].first_point(); - if (! no_reverse) - c.last = src[i].last_point(); - c.idx = i; - endpoints.push_back(c); - } - Polylines retval; - while (! endpoints.empty()) { - // find nearest point - int endpoint_index = nearest_point_index(endpoints, start_near, no_reverse); - assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2); - if (move_from_src) { - retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); - } else { - retval.push_back(src[endpoints[endpoint_index/2].idx]); - } - if (endpoint_index & 1) - retval.back().reverse(); - endpoints.erase(endpoints.begin() + endpoint_index/2); - start_near = retval.back().last_point(); - } - return retval; -} - -Point PolylineCollection::leftmost_point(const Polylines &polylines) -{ - if (polylines.empty()) - throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); - Polylines::const_iterator it = polylines.begin(); - Point p = it->leftmost_point(); - for (++ it; it != polylines.end(); ++it) { - Point p2 = it->leftmost_point(); - if (p2(0) < p(0)) - p = p2; - } - return p; -} - -} // namespace Slic3r diff --git a/src/libslic3r/PolylineCollection.hpp b/src/libslic3r/PolylineCollection.hpp deleted file mode 100644 index 87fc1985b8..0000000000 --- a/src/libslic3r/PolylineCollection.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef slic3r_PolylineCollection_hpp_ -#define slic3r_PolylineCollection_hpp_ - -#include "libslic3r.h" -#include "Polyline.hpp" - -namespace Slic3r { - -class PolylineCollection -{ - static Polylines _chained_path_from( - const Polylines &src, - Point start_near, - bool no_reverse, - bool move_from_src); - -public: - Polylines polylines; - void chained_path(PolylineCollection* retval, bool no_reverse = false) const - { retval->polylines = chained_path(this->polylines, no_reverse); } - void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const - { retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); } - Point leftmost_point() const - { return leftmost_point(polylines); } - void append(const Polylines &polylines) - { this->polylines.insert(this->polylines.end(), polylines.begin(), polylines.end()); } - - static Point leftmost_point(const Polylines &polylines); - static Polylines chained_path(Polylines &&src, bool no_reverse = false) { - return (src.empty() || src.front().points.empty()) ? - Polylines() : - _chained_path_from(src, src.front().first_point(), no_reverse, true); - } - static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false) - { return _chained_path_from(src, start_near, no_reverse, true); } - static Polylines chained_path(const Polylines &src, bool no_reverse = false) { - return (src.empty() || src.front().points.empty()) ? - Polylines() : - _chained_path_from(src, src.front().first_point(), no_reverse, false); - } - static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false) - { return _chained_path_from(src, start_near, no_reverse, false); } -}; - -} - -#endif diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f5601276f0..245b79e807 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -328,17 +328,6 @@ unsigned int Print::num_object_instances() const return instances; } -void Print::_simplify_slices(double distance) -{ - for (PrintObject *object : m_objects) { - for (Layer *layer : object->m_layers) { - layer->slices.simplify(distance); - for (LayerRegion *layerm : layer->regions()) - layerm->slices.simplify(distance); - } - } -} - double Print::max_allowed_layer_height() const { double nozzle_diameter_max = 0.; @@ -1114,6 +1103,9 @@ std::string Print::validate() const if (m_objects.empty()) return L("All objects are outside of the print volume."); + if (extruders().empty()) + return L("The supplied settings will cause an empty print."); + if (m_config.complete_objects) { // Check horizontal clearance. { @@ -1271,10 +1263,7 @@ std::string Print::validate() const } { - // find the smallest nozzle diameter std::vector extruders = this->extruders(); - if (extruders.empty()) - return L("The supplied settings will cause an empty print."); // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits::max(); @@ -1593,7 +1582,7 @@ void Print::_make_skirt() for (const Layer *layer : object->m_layers) { if (layer->print_z > skirt_height_z) break; - for (const ExPolygon &expoly : layer->slices.expolygons) + for (const ExPolygon &expoly : layer->slices) // Collect the outer contour points only, ignore holes for the calculation of the convex hull. append(object_points, expoly.contour.points); } @@ -1704,7 +1693,7 @@ void Print::_make_brim() Polygons islands; for (PrintObject *object : m_objects) { Polygons object_islands; - for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons) + for (ExPolygon &expoly : object->m_layers.front()->slices) object_islands.push_back(expoly.contour); if (! object->support_layers().empty()) object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 5cb13039c2..ce616a1502 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -180,7 +180,6 @@ private: void _slice(const std::vector &layer_height_profile); std::string _fix_slicing_errors(); void _simplify_slices(double distance); - void _make_perimeters(); bool has_support_material() const; void detect_surfaces_type(); void process_external_surfaces(); @@ -383,7 +382,6 @@ private: void _make_skirt(); void _make_brim(); void _make_wipe_tower(); - void _simplify_slices(double distance); // Declared here to have access to Model / ModelObject / ModelInstance static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1ce00f269d..78dd60a4b9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2392,6 +2392,7 @@ void PrintConfigDef::init_sla_params() "the threshold in the middle. This behaviour eliminates " "antialiasing without losing holes in polygons."); def->min = 0; + def->max = 1; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(1.0)); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c4f77b6d7e..d87e63c273 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -117,6 +117,19 @@ void PrintObject::slice() // Simplify slices if required. if (m_print->config().resolution) this->_simplify_slices(scale_(this->print()->config().resolution)); + // Update bounding boxes + tbb::parallel_for( + tbb::blocked_range(0, m_layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + m_print->throw_if_canceled(); + Layer &layer = *m_layers[layer_idx]; + layer.slices_bboxes.clear(); + layer.slices_bboxes.reserve(layer.slices.size()); + for (const ExPolygon &expoly : layer.slices) + layer.slices_bboxes.emplace_back(get_extents(expoly)); + } + }); if (m_layers.empty()) throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); @@ -865,7 +878,7 @@ void PrintObject::process_external_surfaces() // Shrink the holes, let the layer above expand slightly inside the unsupported areas. polygons_append(voids, offset(surface.expolygon, unsupported_width)); } - surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices.expolygons), voids); + surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices), voids); } } ); @@ -975,8 +988,8 @@ void PrintObject::discover_vertical_shells() polygons_append(cache.holes, offset(offset_ex(layer.slices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing)); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices.expolygons)); - svg.draw(layer.slices.expolygons, "blue"); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices)); + svg.draw(layer.slices, "blue"); svg.draw(union_ex(cache.holes), "red"); svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); svg.Close(); @@ -1659,25 +1672,26 @@ void PrintObject::_slice(const std::vector &layer_height_profile) // Trim volumes in a single layer, one by the other, possibly apply upscaling. { Polygons processed; - for (SlicedVolume &sliced_volume : sliced_volumes) { - ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]); - if (upscale) - slices = offset_ex(std::move(slices), delta); - if (! processed.empty()) - // Trim by the slices of already processed regions. - slices = diff_ex(to_polygons(std::move(slices)), processed); - if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size()) - // Collect the already processed regions to trim the to be processed regions. - polygons_append(processed, slices); - sliced_volume.expolygons_by_layer[layer_id] = std::move(slices); - } + for (SlicedVolume &sliced_volume : sliced_volumes) + if (! sliced_volume.expolygons_by_layer.empty()) { + ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]); + if (upscale) + slices = offset_ex(std::move(slices), delta); + if (! processed.empty()) + // Trim by the slices of already processed regions. + slices = diff_ex(to_polygons(std::move(slices)), processed); + if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size()) + // Collect the already processed regions to trim the to be processed regions. + polygons_append(processed, slices); + sliced_volume.expolygons_by_layer[layer_id] = std::move(slices); + } } // Collect and union volumes of a single region. for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { ExPolygons expolygons; size_t num_volumes = 0; for (SlicedVolume &sliced_volume : sliced_volumes) - if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer[layer_id].empty()) { + if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer.empty() && ! sliced_volume.expolygons_by_layer[layer_id].empty()) { ++ num_volumes; append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id])); } @@ -2140,7 +2154,7 @@ std::string PrintObject::_fix_slicing_errors() BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; // remove empty layers from bottom - while (! m_layers.empty() && m_layers.front()->slices.expolygons.empty()) { + while (! m_layers.empty() && m_layers.front()->slices.empty()) { delete m_layers.front(); m_layers.erase(m_layers.begin()); m_layers.front()->lower_layer = nullptr; @@ -2167,115 +2181,17 @@ void PrintObject::_simplify_slices(double distance) Layer *layer = m_layers[layer_idx]; for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx) layer->m_regions[region_idx]->slices.simplify(distance); - layer->slices.simplify(distance); + { + ExPolygons simplified; + for (const ExPolygon& expoly : layer->slices) + expoly.simplify(distance, &simplified); + layer->slices = std::move(simplified); + } } }); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end"; } -void PrintObject::_make_perimeters() -{ - if (! this->set_started(posPerimeters)) - return; - - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); - - // merge slices if they were split into types - if (this->typed_slices) { - for (Layer *layer : m_layers) - layer->merge_slices(); - this->typed_slices = false; - this->invalidate_step(posPrepareInfill); - } - - // compare each layer to the one below, and mark those slices needing - // one additional inner perimeter, like the top of domed objects- - - // this algorithm makes sure that at least one perimeter is overlapping - // but we don't generate any extra perimeter if fill density is zero, as they would be floating - // inside the object - infill_only_where_needed should be the method of choice for printing - // hollow objects - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { - const PrintRegion ®ion = *m_print->regions()[region_id]; - if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) - continue; - - BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; - tbb::parallel_for( - tbb::blocked_range(0, m_layers.size() - 1), - [this, ®ion, region_id](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - LayerRegion &layerm = *m_layers[layer_idx]->regions()[region_id]; - const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->regions()[region_id]; - const Polygons upper_layerm_polygons = upper_layerm.slices; - // Filter upper layer polygons in intersection_ppl by their bounding boxes? - // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; - const double total_loop_length = total_length(upper_layerm_polygons); - const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing(); - const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter); - const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width(); - const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); - - for (Surface &slice : layerm.slices.surfaces) { - for (;;) { - // compute the total thickness of perimeters - const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 - + (region.config().perimeters-1 + slice.extra_perimeters) * perimeter_spacing; - // define a critical area where we don't want the upper slice to fall into - // (it should either lay over our perimeters or outside this area) - const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); - const Polygons critical_area = diff( - offset(slice.expolygon, float(- perimeters_thickness)), - offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth)) - ); - // check whether a portion of the upper slices falls inside the critical area - const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area); - // only add an additional loop if at least 30% of the slice loop would benefit from it - if (total_length(intersection) <= total_loop_length*0.3) - break; - /* - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "extra.svg", - no_arrows => 1, - expolygons => union_ex($critical_area), - polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], - ); - } - */ - ++ slice.extra_perimeters; - } - #ifdef DEBUG - if (slice.extra_perimeters > 0) - printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx); - #endif - } - } - }); - BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; - } - - BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; - tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), - [this](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - m_layers[layer_idx]->make_perimeters(); - } - ); - BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; - - /* - simplify slices (both layer and region slices), - we only need the max resolution for perimeters - ### This makes this method not-idempotent, so we keep it disabled for now. - ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); - */ - - this->set_done(posPerimeters); -} - // Only active if config->infill_only_where_needed. This step trims the sparse infill, // so it acts as an internal support. It maintains all other infill types intact. // Here the internal surfaces and perimeters have to be supported by the sparse infill. @@ -2301,7 +2217,7 @@ void PrintObject::clip_fill_surfaces() // Detect things that we need to support. // Cummulative slices. Polygons slices; - polygons_append(slices, layer->slices.expolygons); + polygons_append(slices, layer->slices); // Cummulative fill surfaces. Polygons fill_surfaces; // Solid surfaces to be supported. diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index b31a9d1f38..a0fb050057 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -4,6 +4,7 @@ #undef assert #endif +#include "clipper.hpp" #include "ShortestPath.hpp" #include "KDTreeIndirect.hpp" #include "MutablePriorityQueue.hpp" @@ -14,6 +15,44 @@ namespace Slic3r { +// Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor. +// This implementation will always produce valid result even if some segments cannot reverse. +template +std::vector> chain_segments_closest_point(std::vector &end_points, KDTreeType &kdtree, CouldReverseFunc &could_reverse_func, EndPointType &first_point) +{ + assert((end_points.size() & 1) == 0); + size_t num_segments = end_points.size() / 2; + assert(num_segments >= 2); + for (EndPointType &ep : end_points) + ep.chain_id = 0; + std::vector> out; + out.reserve(num_segments); + size_t first_point_idx = &first_point - end_points.data(); + out.emplace_back(first_point_idx / 2, (first_point_idx & 1) != 0); + first_point.chain_id = 1; + size_t this_idx = first_point_idx ^ 1; + for (int iter = (int)num_segments - 2; iter >= 0; -- iter) { + EndPointType &this_point = end_points[this_idx]; + this_point.chain_id = 1; + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda). + // Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it. + size_t next_idx = find_closest_point(kdtree, this_point.pos, + [this_idx, &end_points, &could_reverse_func](size_t idx) { + return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1)); + }); + assert(next_idx < end_points.size()); + EndPointType &end_point = end_points[next_idx]; + end_point.chain_id = 1; + this_idx = next_idx ^ 1; + } +#ifndef NDEBUG + assert(end_points[this_idx].chain_id == 0); + for (EndPointType &ep : end_points) + assert(&ep == &end_points[this_idx] || ep.chain_id == 1); +#endif /* NDEBUG */ + return out; +} + // Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm. // Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments. // Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle. @@ -22,8 +61,8 @@ namespace Slic3r { // The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which // is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates // a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities. -template -std::vector> chain_segments(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) +template +std::vector> chain_segments_greedy_constrained_reversals_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) { std::vector> out; @@ -34,7 +73,7 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin { // Just sort the end points so that the first point visited is closest to start_near. out.emplace_back(0, start_near != nullptr && - (end_point_func(0, true) - *start_near).cast().squaredNorm() < (end_point_func(0, false) - *start_near).cast().squaredNorm()); + (end_point_func(0, true) - *start_near).template cast().squaredNorm() < (end_point_func(0, false) - *start_near).template cast().squaredNorm()); } else { @@ -55,8 +94,8 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin std::vector end_points; end_points.reserve(num_segments * 2); for (size_t i = 0; i < num_segments; ++ i) { - end_points.emplace_back(end_point_func(i, true ).cast()); - end_points.emplace_back(end_point_func(i, false).cast()); + end_points.emplace_back(end_point_func(i, true ).template cast()); + end_points.emplace_back(end_point_func(i, false).template cast()); } // Construct the closest point KD tree over end points of segments. @@ -125,13 +164,15 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin EndPoint *first_point = nullptr; size_t first_point_idx = std::numeric_limits::max(); if (start_near != nullptr) { - size_t idx = find_closest_point(kdtree, start_near->cast()); + size_t idx = find_closest_point(kdtree, start_near->template cast()); assert(idx < end_points.size()); first_point = &end_points[idx]; first_point->distance_out = 0.; first_point->chain_id = equivalent_chain.next(); first_point_idx = idx; } + EndPoint *initial_point = first_point; + EndPoint *last_point = nullptr; // Assign the closest point and distance to the end points. for (EndPoint &end_point : end_points) { @@ -240,12 +281,15 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin if (iter == 0) { // Last iteration. There shall be exactly one or two end points waiting to be connected. assert(queue.size() == ((first_point == nullptr) ? 2 : 1)); - if (first_point == nullptr) + if (first_point == nullptr) { first_point = queue.top(); - while (! queue.empty()) { - queue.top()->edge_out = nullptr; queue.pop(); + first_point->edge_out = nullptr; } + last_point = queue.top(); + last_point->edge_out = nullptr; + queue.pop(); + assert(queue.empty()); break; } } else { @@ -280,24 +324,77 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin // Now interconnect pairs of segments into a chain. assert(first_point != nullptr); + out.reserve(num_segments); + bool failed = false; do { assert(out.size() < num_segments); size_t first_point_id = first_point - &end_points.front(); size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; EndPoint *second_point = &end_points[first_point_id ^ 1]; - out.emplace_back(segment_id, (first_point_id & 1) != 0); + if (REVERSE_COULD_FAIL) { + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + } else { + assert(! reverse || could_reverse_func(segment_id)); + } + out.emplace_back(segment_id, reverse); first_point = second_point->edge_out; } while (first_point != nullptr); + if (REVERSE_COULD_FAIL) { + if (failed) { + if (start_near == nullptr) { + // We may try the reverse order. + out.clear(); + first_point = last_point; + failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + } + } + if (failed) + // As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints. + out = chain_segments_closest_point(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front()); + } else { + assert(! failed); + } } assert(out.size() == num_segments); return out; } +template +std::vector> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + return chain_segments_greedy_constrained_reversals_(end_point_func, could_reverse_func, num_segments, start_near); +} + +template +std::vector> chain_segments_greedy(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) +{ + auto could_reverse_func = [](size_t /* idx */) -> bool { return true; }; + return chain_segments_greedy_constrained_reversals_(end_point_func, could_reverse_func, num_segments, start_near); +} + std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near) { auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; - std::vector> out = chain_segments(segment_end_point, entities.size(), start_near); + auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; + std::vector> out = chain_segments_greedy_constrained_reversals(segment_end_point, could_reverse, entities.size(), start_near); for (size_t i = 0; i < entities.size(); ++ i) { ExtrusionEntity *ee = entities[i]; if (ee->is_loop()) @@ -309,7 +406,7 @@ std::vector> chain_extrusion_entities(std::vector &entities, std::vector> &chain) +void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain) { assert(entities.size() == chain.size()); std::vector out; @@ -328,10 +425,34 @@ void chain_and_reorder_extrusion_entities(std::vector &entitie reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); } +std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) +{ + auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; + return chain_segments_greedy(segment_end_point, extrusion_paths.size(), start_near); +} + +void reorder_extrusion_paths(std::vector &extrusion_paths, const std::vector> &chain) +{ + assert(extrusion_paths.size() == chain.size()); + std::vector out; + out.reserve(extrusion_paths.size()); + for (const std::pair &idx : chain) { + out.emplace_back(std::move(extrusion_paths[idx.first])); + if (idx.second) + out.back().reverse(); + } + extrusion_paths.swap(out); +} + +void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) +{ + reorder_extrusion_paths(extrusion_paths, chain_extrusion_paths(extrusion_paths, start_near)); +} + std::vector chain_points(const Points &points, Point *start_near) { auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; }; - std::vector> ordered = chain_segments(segment_end_point, points.size(), start_near); + std::vector> ordered = chain_segments_greedy(segment_end_point, points.size(), start_near); std::vector out; out.reserve(ordered.size()); for (auto &segment_and_reversal : ordered) @@ -339,12 +460,12 @@ std::vector chain_points(const Points &points, Point *start_near) return out; } -Polylines chain_infill_polylines(Polylines &polylines) +Polylines chain_polylines(Polylines &&polylines, const Point *start_near) { auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); }; - std::vector> ordered = chain_segments(segment_end_point, polylines.size(), nullptr); + std::vector> ordered = chain_segments_greedy(segment_end_point, polylines.size(), start_near); Polylines out; - out.reserve(polylines.size()); + out.reserve(polylines.size()); for (auto &segment_and_reversal : ordered) { out.emplace_back(std::move(polylines[segment_and_reversal.first])); if (segment_and_reversal.second) @@ -356,7 +477,7 @@ Polylines chain_infill_polylines(Polylines &polylines) template static inline T chain_path_items(const Points &points, const T &items) { auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; }; - std::vector> ordered = chain_segments(segment_end_point, points.size(), nullptr); + std::vector> ordered = chain_segments_greedy(segment_end_point, points.size(), nullptr); T out; out.reserve(items.size()); for (auto &segment_and_reversal : ordered) @@ -382,7 +503,7 @@ std::vector> chain_print_object_instances(const Print } } auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; }; - std::vector> ordered = chain_segments(segment_end_point, instances.size(), nullptr); + std::vector> ordered = chain_segments_greedy(segment_end_point, instances.size(), nullptr); std::vector> out; out.reserve(instances.size()); for (auto &segment_and_reversal : ordered) diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index b4d0b4737b..cd342015d7 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -15,10 +15,15 @@ namespace Slic3r { std::vector chain_points(const Points &points, Point *start_near = nullptr); std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); -void reorder_extrusion_entities(std::vector &entities, std::vector> &chain); +void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain); void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); -Polylines chain_infill_polylines(Polylines &src); +std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); +void reorder_extrusion_paths(std::vector &extrusion_paths, std::vector> &chain); +void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); + +Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr); +inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); } std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index c0c8e9ea0a..179b35f59e 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -445,8 +445,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t Polygons collect_slices_outer(const Layer &layer) { Polygons out; - out.reserve(out.size() + layer.slices.expolygons.size()); - for (const ExPolygon &expoly : layer.slices.expolygons) + out.reserve(out.size() + layer.slices.size()); + for (const ExPolygon &expoly : layer.slices) out.emplace_back(expoly.contour); return out; } @@ -907,9 +907,13 @@ namespace SupportMaterialInternal { polyline.extend_start(fw); polyline.extend_end(fw); // Is the straight perimeter segment supported at both sides? - if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point())) - // Offset a polyline into a thick line. - polygons_append(bridges, offset(polyline, 0.5f * w + 10.f)); + for (size_t i = 0; i < lower_layer.slices.size(); ++ i) + if (lower_layer.slices_bboxes[i].contains(polyline.first_point()) && lower_layer.slices_bboxes[i].contains(polyline.last_point()) && + lower_layer.slices[i].contains(polyline.first_point()) && lower_layer.slices[i].contains(polyline.last_point())) { + // Offset a polyline into a thick line. + polygons_append(bridges, offset(polyline, 0.5f * w + 10.f)); + break; + } } bridges = union_(bridges); } @@ -923,7 +927,7 @@ namespace SupportMaterialInternal { //FIXME add supports at regular intervals to support long bridges! bridges = diff(bridges, // Offset unsupported edges into polygons. - offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); // Remove bridged areas from the supported areas. contact_polygons = diff(contact_polygons, bridges, true); } @@ -994,7 +998,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // inflate the polygons over and over. Polygons &covered = buildplate_covered[layer_id]; covered = buildplate_covered[layer_id - 1]; - polygons_append(covered, offset(lower_layer.slices.expolygons, scale_(0.01))); + polygons_append(covered, offset(lower_layer.slices, scale_(0.01))); covered = union_(covered, false); // don't apply the safety offset. } } @@ -1023,7 +1027,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ Polygons contact_polygons; Polygons slices_margin_cached; float slices_margin_cached_offset = -1.; - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons); + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices); // Offset of the lower layer, to trim the support polygons with to calculate dense supports. float no_interface_offset = 0.f; if (layer_id == 0) { @@ -1162,7 +1166,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ slices_margin_cached_offset = slices_margin_offset; slices_margin_cached = (slices_margin_offset == 0.f) ? lower_layer_polygons : - offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); + offset2(to_polygons(lower_layer.slices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); if (! buildplate_covered.empty()) { // Trim the inflated contact surfaces by the top surfaces as well. polygons_append(slices_margin_cached, buildplate_covered[layer_id]); @@ -1468,7 +1472,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta svg.draw(union_ex(top, false), "blue", 0.5f); svg.draw(union_ex(projection_raw, true), "red", 0.5f); svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f)); - svg.draw(layer.slices.expolygons, "green", 0.5f); + svg.draw(layer.slices, "green", 0.5f); } #endif /* SLIC3R_DEBUG */ @@ -1568,8 +1572,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta Polygons &layer_support_area = layer_support_areas[layer_id]; task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area, layer_id] { // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. - // Polygons trimming = union_(to_polygons(layer.slices.expolygons), touching, true); - Polygons trimming = offset(layer.slices.expolygons, float(SCALED_EPSILON)); + // Polygons trimming = union_(to_polygons(layer.slices), touching, true); + Polygons trimming = offset(layer.slices, float(SCALED_EPSILON)); projection = diff(projection_raw, trimming, false); #ifdef SLIC3R_DEBUG { @@ -2101,7 +2105,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( const Layer &object_layer = *object.layers()[i]; if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) break; - polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + polygons_append(polygons_trimming, offset(object_layer.slices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } if (! m_slicing_params.soluble_interface) { // Collect all bottom surfaces, which will be extruded with a bridging flow. @@ -2214,7 +2218,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf // Expand the bases of the support columns in the 1st layer. columns_base->polygons = diff( offset(columns_base->polygons, inflate_factor_1st_layer), - offset(m_object->layers().front()->slices.expolygons, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + offset(m_object->layers().front()->slices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (contacts != nullptr) columns_base->polygons = diff(columns_base->polygons, interface_polygons); } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 8094cdde10..0e127a868c 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1717,13 +1717,18 @@ static void thick_point_to_verts(const Vec3crd& point, point_to_indexed_vertex_array(point, width, height, volume.indexed_vertex_array); } +void _3DScene::extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume) +{ + if (polyline.size() >= 2) { + size_t num_segments = polyline.size() - 1; + thick_lines_to_verts(polyline.lines(), std::vector(num_segments, width), std::vector(num_segments, height), false, print_z, volume); + } +} + // Fill in the qverts and tverts with quads and triangles for the extrusion_path. void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) { - Lines lines = extrusion_path.polyline.lines(); - std::vector widths(lines.size(), extrusion_path.width); - std::vector heights(lines.size(), extrusion_path.height); - thick_lines_to_verts(lines, widths, heights, false, print_z, volume); + extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume); } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 8c1d68f77b..8c5040eee2 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -656,6 +656,7 @@ public: static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); + static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index c9cdff1628..9ca08809b0 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -354,8 +354,7 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) toggle_field("pad_wall_slope", pad_en); toggle_field("pad_around_object", pad_en); - bool has_suppad = pad_en && supports_en; - bool zero_elev = config->opt_bool("pad_around_object") && has_suppad; + bool zero_elev = config->opt_bool("pad_around_object") && pad_en; toggle_field("support_object_elevation", supports_en && !zero_elev); toggle_field("pad_object_gap", zero_elev); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5b1188169e..ff70fd451a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4987,19 +4987,21 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat // helper functions to select data in dependence of the extrusion view type struct Helper { - static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) + static float path_filter(GCodePreviewData::Extrusion::EViewType type, const GCodePreviewData::Extrusion::Path& path) { switch (type) { case GCodePreviewData::Extrusion::FeatureType: // The role here is used for coloring. - return (float)path.role(); + return (float)path.extrusion_role; case GCodePreviewData::Extrusion::Height: return path.height; case GCodePreviewData::Extrusion::Width: return path.width; case GCodePreviewData::Extrusion::Feedrate: return path.feedrate; + case GCodePreviewData::Extrusion::FanSpeed: + return path.fan_speed; case GCodePreviewData::Extrusion::VolumetricRate: return path.feedrate * (float)path.mm3_per_mm; case GCodePreviewData::Extrusion::Tool: @@ -5025,6 +5027,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat return data.get_width_color(value); case GCodePreviewData::Extrusion::Feedrate: return data.get_feedrate_color(value); + case GCodePreviewData::Extrusion::FanSpeed: + return data.get_fan_speed_color(value); case GCodePreviewData::Extrusion::VolumetricRate: return data.get_volumetric_rate_color(value); case GCodePreviewData::Extrusion::Tool: @@ -5067,15 +5071,15 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat { std::vector num_paths_per_role(size_t(erCount), 0); for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers) - for (const ExtrusionPath &path : layer.paths) - ++ num_paths_per_role[size_t(path.role())]; + for (const GCodePreviewData::Extrusion::Path &path : layer.paths) + ++ num_paths_per_role[size_t(path.extrusion_role)]; std::vector> roles_values; roles_values.assign(size_t(erCount), std::vector()); for (size_t i = 0; i < roles_values.size(); ++ i) roles_values[i].reserve(num_paths_per_role[i]); for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - for (const ExtrusionPath& path : layer.paths) - roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); + for (const GCodePreviewData::Extrusion::Path &path : layer.paths) + roles_values[size_t(path.extrusion_role)].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); roles_filters.reserve(size_t(erCount)); size_t num_buffers = 0; for (std::vector &values : roles_values) { @@ -5103,9 +5107,9 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat // populates volumes for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) { - for (const ExtrusionPath& path : layer.paths) + for (const GCodePreviewData::Extrusion::Path& path : layer.paths) { - std::vector> &filters = roles_filters[size_t(path.role())]; + std::vector> &filters = roles_filters[size_t(path.extrusion_role)]; auto key = std::make_pair(Helper::path_filter(preview_data.extrusion.view_type, path), nullptr); auto it_filter = std::lower_bound(filters.begin(), filters.end(), key); assert(it_filter != filters.end() && key.first == it_filter->first); @@ -5115,7 +5119,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - _3DScene::extrusionentity_to_verts(path, layer.z, vol); + _3DScene::extrusionentity_to_verts(path.polyline, path.width, path.height, layer.z, vol); } // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. for (std::vector> &filters : roles_filters) { diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 7d650dcd15..a59156ffff 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -131,7 +131,7 @@ ObjectList::ObjectList(wxWindow* parent) : { wxDataViewItemArray sels; GetSelections(sels); - if (sels.front() == m_last_selected_item) + if (! sels.empty() && sels.front() == m_last_selected_item) m_last_selected_item = sels.back(); else m_last_selected_item = event.GetItem(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 7c761ed5fe..b6350bcabc 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -221,6 +221,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_choice_view_type->Append(_(L("Height"))); m_choice_view_type->Append(_(L("Width"))); m_choice_view_type->Append(_(L("Speed"))); + m_choice_view_type->Append(_(L("Fan speed"))); m_choice_view_type->Append(_(L("Volumetric flow rate"))); m_choice_view_type->Append(_(L("Tool"))); m_choice_view_type->Append(_(L("Color Print"))); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 803a5cf082..d3f331442f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1808,7 +1808,10 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("single_extruder_multi_material"); optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { - size_t extruders_count = boost::any_cast(optgroup->get_value("extruders_count")); + // optgroup->get_value() return int for def.type == coInt, + // Thus, there should be boost::any_cast ! + // Otherwise, boost::any_cast causes an "unhandled unknown exception" + size_t extruders_count = size_t(boost::any_cast(optgroup->get_value("extruders_count"))); wxTheApp->CallAfter([this, opt_key, value, extruders_count]() { if (opt_key == "extruders_count" || opt_key == "single_extruder_multi_material") { extruders_count_changed(extruders_count); diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index 1f16ae3d4b..a01788c56c 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -14,13 +14,17 @@ void clear(); ExtrusionEntityCollection* chained_path(bool no_reverse, ExtrusionRole role = erMixed) %code{% + if (no_reverse) + croak("no_reverse must be false"); RETVAL = new ExtrusionEntityCollection(); - THIS->chained_path(RETVAL, no_reverse, role); + *RETVAL = THIS->chained_path_from(THIS->entities.front()->first_point()); %}; ExtrusionEntityCollection* chained_path_from(Point* start_near, bool no_reverse, ExtrusionRole role = erMixed) %code{% + if (no_reverse) + croak("no_reverse must be false"); RETVAL = new ExtrusionEntityCollection(); - THIS->chained_path_from(*start_near, RETVAL, no_reverse, role); + *RETVAL = THIS->chained_path_from(*start_near, role); %}; Clone first_point(); Clone last_point(); diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index 34a6d33bee..647d851eb1 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -3,7 +3,6 @@ %{ #include #include "libslic3r/Fill/Fill.hpp" -#include "libslic3r/PolylineCollection.hpp" #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" %} diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index efd6c9ae63..6f3164707a 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -3,6 +3,7 @@ %{ #include #include "libslic3r/Layer.hpp" +#include "libslic3r/ExPolygonCollection.hpp" %} %name{Slic3r::Layer::Region} class LayerRegion { @@ -19,8 +20,6 @@ %code%{ RETVAL = &THIS->fill_surfaces; %}; Polygons bridged() %code%{ RETVAL = THIS->bridged; %}; - Ref unsupported_bridge_edges() - %code%{ RETVAL = &THIS->unsupported_bridge_edges; %}; Ref perimeters() %code%{ RETVAL = &THIS->perimeters; %}; Ref fills() @@ -61,8 +60,8 @@ Ref get_region(int idx); Ref add_region(PrintRegion* print_region); - Ref slices() - %code%{ RETVAL = &THIS->slices; %}; + ExPolygonCollection* slices() + %code%{ RETVAL = new ExPolygonCollection(THIS->slices); %}; int ptr() %code%{ RETVAL = (int)(intptr_t)THIS; %}; @@ -110,8 +109,8 @@ Ref get_region(int idx); Ref add_region(PrintRegion* print_region); - Ref slices() - %code%{ RETVAL = &THIS->slices; %}; + ExPolygonCollection* slices() + %code%{ RETVAL = new ExPolygonCollection(THIS->slices); %}; void export_region_slices_to_svg(const char *path); void export_region_fill_surfaces_to_svg(const char *path); diff --git a/xs/xsp/PerimeterGenerator.xsp b/xs/xsp/PerimeterGenerator.xsp index 2e8213795a..07059de614 100644 --- a/xs/xsp/PerimeterGenerator.xsp +++ b/xs/xsp/PerimeterGenerator.xsp @@ -19,7 +19,7 @@ ~PerimeterGenerator(); void set_lower_slices(ExPolygonCollection* lower_slices) - %code{% THIS->lower_slices = lower_slices; %}; + %code{% THIS->lower_slices = &lower_slices->expolygons; %}; void set_layer_id(int layer_id) %code{% THIS->layer_id = layer_id; %}; void set_perimeter_flow(Flow* flow) diff --git a/xs/xsp/PolylineCollection.xsp b/xs/xsp/PolylineCollection.xsp index 7237be5bb1..d8bb41b497 100644 --- a/xs/xsp/PolylineCollection.xsp +++ b/xs/xsp/PolylineCollection.xsp @@ -2,7 +2,11 @@ %{ #include -#include "libslic3r/PolylineCollection.hpp" + +#include "libslic3r.h" +#include "Polyline.hpp" +#include "ShortestPath.hpp" + %} %name{Slic3r::Polyline::Collection} class PolylineCollection { @@ -14,16 +18,15 @@ PolylineCollection* chained_path(bool no_reverse) %code{% RETVAL = new PolylineCollection(); - THIS->chained_path(RETVAL, no_reverse); + RETVAL->polylines = chain_polylines(THIS->polylines, &THIS->polylines.front().first_point()); %}; PolylineCollection* chained_path_from(Point* start_near, bool no_reverse) %code{% RETVAL = new PolylineCollection(); - THIS->chained_path_from(*start_near, RETVAL, no_reverse); + RETVAL->polylines = chain_polylines(THIS->polylines, start_near); %}; int count() %code{% RETVAL = THIS->polylines.size(); %}; - Clone leftmost_point(); %{ PolylineCollection*