diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 117090dd82..fa0edec2ba 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -89,33 +89,34 @@ struct stl_neighbors { }; struct stl_stats { - stl_stats() { this->reset(); } - void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; } - char header[81]; - stl_type type; - uint32_t number_of_facets; - stl_vertex max; - stl_vertex min; - stl_vertex size; - float bounding_diameter; - float shortest_edge; - float volume; - int connected_edges; - int connected_facets_1_edge; - int connected_facets_2_edge; - int connected_facets_3_edge; - int facets_w_1_bad_edge; - int facets_w_2_bad_edge; - int facets_w_3_bad_edge; - int original_num_facets; - int edges_fixed; - int degenerate_facets; - int facets_removed; - int facets_added; - int facets_reversed; - int backwards_edges; - int normals_fixed; - int number_of_parts; + stl_stats() { memset(&header, 0, 81); } + char header[81]; + stl_type type = (stl_type)0; + uint32_t number_of_facets = 0; + stl_vertex max = stl_vertex::Zero(); + stl_vertex min = stl_vertex::Zero(); + stl_vertex size = stl_vertex::Zero(); + float bounding_diameter = 0.f; + float shortest_edge = 0.f; + float volume = -1.f; + int connected_edges = 0; + int connected_facets_1_edge = 0; + int connected_facets_2_edge = 0; + int connected_facets_3_edge = 0; + int facets_w_1_bad_edge = 0; + int facets_w_2_bad_edge = 0; + int facets_w_3_bad_edge = 0; + int original_num_facets = 0; + int edges_fixed = 0; + int degenerate_facets = 0; + int facets_removed = 0; + int facets_added = 0; + int facets_reversed = 0; + int backwards_edges = 0; + int normals_fixed = 0; + int number_of_parts = 0; + + void clear() { *this = stl_stats(); } }; struct stl_file { @@ -124,7 +125,7 @@ struct stl_file { void clear() { this->facet_start.clear(); this->neighbors_start.clear(); - this->stats.reset(); + this->stats.clear(); } size_t memsize() const { diff --git a/src/clipper/clipper_z.hpp b/src/clipper/clipper_z.hpp index 0f31ac11c5..e5e7d48ce1 100644 --- a/src/clipper/clipper_z.hpp +++ b/src/clipper/clipper_z.hpp @@ -15,4 +15,4 @@ #undef clipper_hpp #undef use_xyz -#endif clipper_z_hpp +#endif // clipper_z_hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 97e0fc09b0..5ca87717d6 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 -# KdTree.hpp + KDTreeIndirect.hpp Layer.cpp Layer.hpp LayerRegion.cpp @@ -142,6 +142,8 @@ add_library(libslic3r STATIC PrintObject.cpp PrintRegion.cpp Semver.cpp + ShortestPath.cpp + ShortestPath.hpp SLAPrint.cpp SLAPrint.hpp SLA/SLAAutoSupports.hpp diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 4c6e542f4d..c3183bd8e1 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1,5 +1,6 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" +#include "ShortestPath.hpp" // #define CLIPPER_UTILS_DEBUG @@ -671,21 +672,19 @@ void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) // collect ordering points Points ordering_points; ordering_points.reserve(nodes.size()); - for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { - Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); - ordering_points.emplace_back(p); - } + for (ClipperLib::PolyNode *pn : nodes) + ordering_points.emplace_back(Point(pn->Contour.front().X, pn->Contour.front().Y)); // perform the ordering - ClipperLib::PolyNodes ordered_nodes; - Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes); - + ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes); + // push results recursively - for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { + for (ClipperLib::PolyNode *pn : ordered_nodes) { // traverse the next depth - traverse_pt((*it)->Childs, retval); - retval->emplace_back(ClipperPath_to_Slic3rPolygon((*it)->Contour)); - if ((*it)->IsHole()) retval->back().reverse(); // ccw + traverse_pt(pn->Childs, retval); + retval->emplace_back(ClipperPath_to_Slic3rPolygon(pn->Contour)); + if (pn->IsHole()) + retval->back().reverse(); // ccw } } diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 5776980714..9aab3a0eb7 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -249,7 +249,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const // Special case: For a DynamicConfig, convert a templated enum to a generic enum. new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) : this->default_value->clone(); - return this->create_empty_option(); + return this->create_empty_option(); } // Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread! diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index c349ad3e18..334593ab5f 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -353,7 +353,7 @@ public: bool apply_override(const ConfigOption *rhs) override { if (this->nullable()) throw std::runtime_error("Cannot override a nullable ConfigOption."); - if (rhs->type() != this->type()) + if (rhs->type() != this->type()) throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) { @@ -461,7 +461,7 @@ public: for (const double &v : this->values) { if (&v != &this->values.front()) ss << ","; - serialize_single_value(ss, v); + serialize_single_value(ss, v); } return ss.str(); } @@ -607,7 +607,7 @@ public: for (const int &v : this->values) { if (&v != &this->values.front()) ss << ","; - serialize_single_value(ss, v); + serialize_single_value(ss, v); } return ss.str(); } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index ce52ae1526..dfc180689b 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -5,6 +5,8 @@ #include "Polygon.hpp" #include "Polyline.hpp" +#include + namespace Slic3r { class ExPolygonCollection; diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 70c2348afa..f7fab1ba1b 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -16,7 +16,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionE this->entities = other.entities; for (size_t i = 0; i < this->entities.size(); ++i) this->entities[i] = this->entities[i]->clone(); - this->orig_indices = other.orig_indices; this->no_sort = other.no_sort; return *this; } @@ -24,7 +23,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionE void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c) { std::swap(this->entities, c.entities); - std::swap(this->orig_indices, c.orig_indices); std::swap(this->no_sort, c.no_sort); } @@ -82,10 +80,10 @@ ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_revers return coll; } -void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector* orig_indices) const +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, orig_indices); + 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 @@ -95,7 +93,7 @@ ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point sta return coll; } -void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector* orig_indices) const +void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const { if (this->no_sort) { *retval = *this; @@ -103,7 +101,6 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt } retval->entities.reserve(this->entities.size()); - retval->orig_indices.reserve(this->entities.size()); // if we're asked to return the original indices, build a map std::map indices_map; @@ -122,8 +119,8 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt ExtrusionEntity *entity = entity_src->clone(); my_paths.push_back(entity); - if (orig_indices != nullptr) - indices_map[entity] = &entity_src - &this->entities.front(); +// if (orig_indices != nullptr) +// indices_map[entity] = &entity_src - &this->entities.front(); } Points endpoints; @@ -142,8 +139,8 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt 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]); +// 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(); diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 221afc4536..e92fa156f5 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -14,15 +14,14 @@ public: ExtrusionEntity* clone_move() override { return new ExtrusionEntityCollection(std::move(*this)); } ExtrusionEntitiesPtr entities; // we own these entities - std::vector orig_indices; // handy for XS bool no_sort; ExtrusionEntityCollection(): no_sort(false) {}; - ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : orig_indices(other.orig_indices), no_sort(other.no_sort) { this->append(other.entities); } - ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), orig_indices(std::move(other.orig_indices)), no_sort(other.no_sort) {} + ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort) { this->append(other.entities); } + ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {} explicit ExtrusionEntityCollection(const ExtrusionPaths &paths); ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other); ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) - { this->entities = std::move(other.entities); this->orig_indices = std::move(other.orig_indices); this->no_sort = other.no_sort; return *this; } + { this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; } ~ExtrusionEntityCollection() { clear(); } explicit operator ExtrusionPaths() const; @@ -67,9 +66,9 @@ 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, std::vector* orig_indices = nullptr) 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, std::vector* orig_indices = nullptr) const; + void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, 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(); } diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index ad308adab3..ab20bbddb9 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -15,40 +15,39 @@ namespace Slic3r { struct SurfaceFillParams { - SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); } // Zero based extruder ID. - unsigned int extruder; + unsigned int extruder = 0; // Infill pattern, adjusted for the density etc. - InfillPattern pattern; + InfillPattern pattern = InfillPattern(0); // FillBase // in unscaled coordinates - coordf_t spacing; + coordf_t spacing = 0.; // infill / perimeter overlap, in unscaled coordinates - coordf_t overlap; + coordf_t overlap = 0.; // Angle as provided by the region config, in radians. - float angle; + float angle = 0.f; // Non-negative for a bridge. - float bridge_angle; + float bridge_angle = 0.f; // FillParams - float density; + float density = 0.f; // Don't connect the fill lines around the inner perimeter. - bool dont_connect; + bool dont_connect = false; // Don't adjust spacing to fill the space evenly. - bool dont_adjust; + bool dont_adjust = false; // width, height of extrusion, nozzle diameter, is bridge // For the output, for fill generator. - Flow flow; + Flow flow = Flow(0.f, 0.f, 0.f, false); // For the output - ExtrusionRole extrusion_role; + ExtrusionRole extrusion_role = ExtrusionRole(0); // Various print settings? // Index of this entry in a linear vector. - size_t idx; + size_t idx = 0; bool operator<(const SurfaceFillParams &rhs) const { diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 6a37e4369f..44267d3a47 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -1,5 +1,5 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include "Fill3DHoneycomb.hpp" @@ -175,27 +175,24 @@ void Fill3DHoneycomb::_fill_surface_single( std::swap(expolygon_off, expolygons_off.front()); } } - Polylines chained = PolylineCollection::chained_path_from( - std::move(polylines), - PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; - for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { + for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; - const Point &first_point = it_polyline->points.front(); + const Point &first_point = polyline.points.front(); const Point &last_point = pts_end.back(); // TODO: we should also check that both points are on a fill_boundary to avoid // connecting paths on the boundaries of internal regions if ((last_point - first_point).cast().norm() <= 1.5 * distance && expolygon_off.contains(Line(last_point, first_point))) { // Append the polyline. - pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); + pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); continue; } } // The lines cannot be connected. - polylines_out.emplace_back(std::move(*it_polyline)); + polylines_out.emplace_back(std::move(polyline)); first = false; } } diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 04319bb268..09f0782bc9 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -1,5 +1,5 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include #include @@ -166,11 +166,8 @@ void FillGyroid::_fill_surface_single( std::swap(expolygon_off, expolygons_off.front()); } } - Polylines chained = PolylineCollection::chained_path_from( - std::move(polylines), - PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; - for (Polyline &polyline : chained) { + for (Polyline &polyline : chain_infill_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 cbfe926f2f..be569c2d84 100644 --- a/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/src/libslic3r/Fill/FillHoneycomb.cpp @@ -1,5 +1,5 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include "FillHoneycomb.hpp" @@ -93,22 +93,20 @@ void FillHoneycomb::_fill_surface_single( // connect paths if (! paths.empty()) { // prevent calling leftmost_point() on empty collections - Polylines chained = PolylineCollection::chained_path_from( - std::move(paths), - PolylineCollection::leftmost_point(paths), false); + Polylines chained = chain_infill_polylines(std::move(paths)); assert(paths.empty()); paths.clear(); - for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) { + for (Polyline &path : chained) { if (! paths.empty()) { // distance between first point of this path and last point of last path - double distance = (it_path->first_point() - paths.back().last_point()).cast().norm(); + double distance = (path.first_point() - paths.back().last_point()).cast().norm(); if (distance <= m.hex_width) { - paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end()); + paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end()); continue; } } // Don't connect the paths. - paths.push_back(*it_path); + paths.push_back(std::move(path)); } } diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 205eb1b669..d535ba29b5 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -1,6 +1,6 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" -#include "../PolylineCollection.hpp" +#include "../ShortestPath.hpp" #include "../Surface.hpp" #include "FillRectilinear.hpp" @@ -92,15 +92,12 @@ void FillRectilinear::_fill_surface_single( std::swap(expolygon_off, expolygons_off.front()); } } - Polylines chained = PolylineCollection::chained_path_from( - std::move(polylines), - PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; - for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { + for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; - const Point &first_point = it_polyline->points.front(); + const Point &first_point = polyline.points.front(); const Point &last_point = pts_end.back(); // Distance in X, Y. const Vector distance = last_point - first_point; @@ -109,12 +106,12 @@ void FillRectilinear::_fill_surface_single( if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) && expolygon_off.contains(Line(last_point, first_point))) { // Append the polyline. - pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); + pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); continue; } } // The lines cannot be connected. - polylines_out.emplace_back(std::move(*it_polyline)); + polylines_out.emplace_back(std::move(polyline)); first = false; } } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 92c958d8ab..8989487ccc 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -979,7 +979,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << layer_height_profile.front(); for (size_t i = 1; i < layer_height_profile.size(); ++i) stream << ";" << layer_height_profile[i]; - stream << "\n \n"; + stream << "\n \n"; } // Export layer height ranges including the layer range specific config overrides. diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 03ea71a83e..d6f87197df 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -246,7 +246,7 @@ static void extract_model_from_archive( sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) { // Normal was mangled. Maybe denormals or "not a number" were stored? // Just reset the normal and silently ignore it. - memset(&facet.normal, 0, sizeof(facet.normal)); + facet.normal = stl_normal::Zero(); } facets.emplace_back(facet); } @@ -278,7 +278,7 @@ static void extract_model_from_archive( instance->set_rotation(instance_rotation); instance->set_scaling_factor(instance_scaling_factor); instance->set_offset(instance_offset); - if (group_id != (size_t)-1) + if (group_id != (unsigned int)(-1)) group_to_model_object[group_id] = model_object; } else { // This is not the 1st mesh of a group. Add it to the ModelObject. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0ccc3ddf57..348834b3c4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6,6 +6,7 @@ #include "Geometry.hpp" #include "GCode/PrintExtents.hpp" #include "GCode/WipeTower.hpp" +#include "ShortestPath.hpp" #include "Utils.hpp" #include @@ -659,7 +660,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path))) return; - print->set_started(psGCodeExport); + print->set_started(psGCodeExport); BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info(); @@ -1160,7 +1161,7 @@ void GCode::_do_export(Print &print, FILE *file) for (const LayerToPrint <p : layers_to_print) { std::vector lrs; lrs.emplace_back(std::move(ltp)); - this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object.copies().data()); + this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, © - object.copies().data()); print.throw_if_canceled(); } #ifdef HAS_PRESSURE_EQUALIZER @@ -1174,12 +1175,8 @@ void GCode::_do_export(Print &print, FILE *file) } } } else { - // Order objects using a nearest neighbor search. - std::vector object_indices; - Points object_reference_points; - for (PrintObject *object : print.objects()) - object_reference_points.push_back(object->copies().front()); - Slic3r::Geometry::chained_path(object_reference_points, object_indices); + // Order object instances using a nearest neighbor search. + std::vector> print_object_instances_ordering = chain_print_object_instances(print); // Sort layers by Z. // All extrusion moves with the same top layer height are extruded uninterrupted. std::vector>> layers_to_print = collect_layers_to_print(print); @@ -1218,7 +1215,7 @@ void GCode::_do_export(Print &print, FILE *file) const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first); if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); - this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); + this->process_layer(file, print, layer.second, layer_tools, &print_object_instances_ordering, size_t(-1)); print.throw_if_canceled(); } #ifdef HAS_PRESSURE_EQUALIZER @@ -1415,7 +1412,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc // Skip the rest of the line. for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr); // Skip the end of line indicators. - for (; *ptr == '\r' || *ptr == '\n'; ++ ptr); + for (; *ptr == '\r' || *ptr == '\n'; ++ ptr); } return temp_set_by_gcode; } @@ -1529,8 +1526,54 @@ inline std::vector& object_islands_by_extruder( return islands; } +std::vector GCode::sort_print_object_instances( + std::vector &objects_by_extruder, + const std::vector &layers, + // Ordering must be defined for normal (non-sequential print). + const std::vector> *ordering, + // For sequential print, the instance of the object to be printing has to be defined. + const size_t single_object_instance_idx) +{ + std::vector out; + + if (ordering == nullptr) { + // Sequential print, single object is being printed. + for (ObjectByExtruder &object_by_extruder : objects_by_extruder) { + const size_t layer_id = &object_by_extruder - objects_by_extruder.data(); + const PrintObject *print_object = layers[layer_id].object(); + if (print_object) + out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx); + } + } else { + // Create mapping from PrintObject* to ObjectByExtruder*. + std::vector> sorted; + sorted.reserve(objects_by_extruder.size()); + for (ObjectByExtruder &object_by_extruder : objects_by_extruder) { + const size_t layer_id = &object_by_extruder - objects_by_extruder.data(); + const PrintObject *print_object = layers[layer_id].object(); + if (print_object) + sorted.emplace_back(print_object, &object_by_extruder); + } + std::sort(sorted.begin(), sorted.end()); + + if (! sorted.empty()) { + const Print &print = *sorted.front().first->print(); + out.reserve(sorted.size()); + for (const std::pair &instance_id : *ordering) { + const PrintObject &print_object = *print.objects()[instance_id.first]; + std::pair key(&print_object, nullptr); + auto it = std::lower_bound(sorted.begin(), sorted.end(), key); + if (it != sorted.end() && it->first == &print_object) + // ObjectByExtruder for this PrintObject was found. + out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second); + } + } + } + return out; +} + // In sequential mode, process_layer is called once per each object and its copy, -// therefore layers will contain a single entry and single_object_idx will point to the copy of the object. +// therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths // and performing the extruder specific extrusions together. @@ -1541,14 +1584,16 @@ void GCode::process_layer( // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, const LayerTools &layer_tools, + // Pairs of PrintObject index and its instance index. + const std::vector> *ordering, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. - const size_t single_object_idx) + const size_t single_object_instance_idx) { assert(! layers.empty()); // assert(! layer_tools.extruders.empty()); // Either printing all copies of all objects, or just a single copy of a single object. - assert(single_object_idx == size_t(-1) || layers.size() == 1); + assert(single_object_instance_idx == size_t(-1) || layers.size() == 1); if (layer_tools.extruders.empty()) // Nothing to extrude. @@ -1762,6 +1807,17 @@ void GCode::process_layer( layer_surface_bboxes.reserve(n_slices); for (const ExPolygon &expoly : layer.slices.expolygons) layer_surface_bboxes.push_back(get_extents(expoly.contour)); + // 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; + slices_test_order.reserve(n_slices); + for (size_t i = 0; i < n_slices; ++ i) + slices_test_order.emplace_back(i); + std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](int i, int j) { + const Vec2d s1 = layer_surface_bboxes[i].size().cast(); + const Vec2d s2 = layer_surface_bboxes[j].size().cast(); + return s1.x() * s1.y() < s2.x() * s2.y(); + }); auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) { const BoundingBox &bbox = layer_surface_bboxes[i]; return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && @@ -1809,16 +1865,19 @@ void GCode::process_layer( extruder, &layer_to_print - layers.data(), layers.size(), n_slices+1); - for (size_t i = 0; i <= n_slices; ++i) + for (size_t i = 0; i <= n_slices; ++ i) { + bool last = i == n_slices; + size_t island_idx = last ? n_slices : slices_test_order[i]; if (// fill->first_point does not fit inside any slice - i == n_slices || + last || // fill->first_point fits inside ith slice - point_inside_surface(i, fill->first_point())) { - if (islands[i].by_region.empty()) - islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); - islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); + point_inside_surface(island_idx, fill->first_point())) { + if (islands[island_idx].by_region.empty()) + islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); + islands[island_idx].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); break; } + } } } } @@ -1883,62 +1942,49 @@ void GCode::process_layer( if (objects_by_extruder_it == by_extruder.end()) continue; + std::vector instances_to_print = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering, single_object_instance_idx); + // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): bool is_anything_overridden = const_cast(layer_tools).wiping_extrusions().is_anything_overridden(); for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) { if (is_anything_overridden && print_wipe_extrusions == 0) gcode+="; PURGING FINISHED\n"; - for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) { - const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data(); - const PrintObject *print_object = layers[layer_id].object(); - if (print_object == nullptr) - // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z. - continue; - - m_config.apply(print_object->config(), true); - m_layer = layers[layer_id].layer(); + for (InstanceToPrint &instance_to_print : instances_to_print) { + m_config.apply(instance_to_print.print_object.config(), true); + m_layer = layers[instance_to_print.layer_id].layer(); if (m_config.avoid_crossing_perimeters) m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true)); - Points copies; - if (single_object_idx == size_t(-1)) - copies = print_object->copies(); - else - copies.push_back(print_object->copies()[single_object_idx]); - // Sort the copies by the closest point starting with the current print position. - unsigned int copy_id = 0; - for (const Point © : copies) { - if (this->config().gcode_label_objects) - gcode += std::string("; printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n"; - // When starting a new object, use the external motion planner for the first travel move. - std::pair this_object_copy(print_object, copy); - if (m_last_obj_copy != this_object_copy) - m_avoid_crossing_perimeters.use_external_mp_once = true; - m_last_obj_copy = this_object_copy; - this->set_origin(unscale(copy)); - if (object_by_extruder.support != nullptr && !print_wipe_extrusions) { - m_layer = layers[layer_id].support_layer; - gcode += this->extrude_support( - // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. - object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role)); - m_layer = layers[layer_id].layer(); - } - for (ObjectByExtruder::Island &island : object_by_extruder.islands) { - const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region; - - if (print.config().infill_first) { - gcode += this->extrude_infill(print, by_region_specific); - gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]); - } else { - gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]); - gcode += this->extrude_infill(print,by_region_specific); - } - } - if (this->config().gcode_label_objects) - gcode += std::string("; stop printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n"; - ++ copy_id; + if (this->config().gcode_label_objects) + gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; + // When starting a new object, use the external motion planner for the first travel move. + const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id]; + std::pair this_object_copy(&instance_to_print.print_object, offset); + if (m_last_obj_copy != this_object_copy) + m_avoid_crossing_perimeters.use_external_mp_once = true; + m_last_obj_copy = this_object_copy; + this->set_origin(unscale(offset)); + if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) { + 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)); + m_layer = layers[instance_to_print.layer_id].layer(); } + for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { + const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region; + + if (print.config().infill_first) { + gcode += this->extrude_infill(print, by_region_specific); + gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]); + } else { + gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]); + gcode += this->extrude_infill(print,by_region_specific); + } + } + if (this->config().gcode_label_objects) + gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; } } } @@ -2542,12 +2588,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig()); - ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); - for (ExtrusionEntity *fill : chained.entities) { + for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) { auto *eec = dynamic_cast(fill); if (eec) { - ExtrusionEntityCollection chained2 = eec->chained_path_from(m_last_pos, false); - for (ExtrusionEntity *ee : chained2.entities) + for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities) gcode += this->extrude_entity(*ee, "infill"); } else gcode += this->extrude_entity(*fill, "infill"); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 72813810bd..45ff7eda65 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -202,7 +202,7 @@ protected: const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; } coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; } }; - static std::vector collect_layers_to_print(const PrintObject &object); + static std::vector collect_layers_to_print(const PrintObject &object); static std::vector>> collect_layers_to_print(const Print &print); void process_layer( // Write into the output file. @@ -210,7 +210,9 @@ protected: const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, - const LayerTools &layer_tools, + const LayerTools &layer_tools, + // Pairs of PrintObject index and its instance index. + const std::vector> *ordering, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx = size_t(-1)); @@ -258,6 +260,25 @@ protected: std::vector islands; }; + struct InstanceToPrint + { + InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) : + object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {} + + ObjectByExtruder &object_by_extruder; + const size_t layer_id; + const PrintObject &print_object; + // Instance idx of the copy of a print object. + const size_t instance_id; + }; + + std::vector sort_print_object_instances( + std::vector &objects_by_extruder, + const std::vector &layers, + // Ordering must be defined for normal (non-sequential print). + const std::vector> *ordering, + // For sequential print, the instance of the object to be printing has to be defined. + const size_t single_object_instance_idx); std::string extrude_perimeters(const Print &print, const std::vector &by_region, std::unique_ptr &lower_layer_edge_grid); std::string extrude_infill(const Print &print, const std::vector &by_region); diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 0a9ec320ef..b7d1d57dfa 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -308,7 +308,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z)); // Find the 1st layer above lt_new. for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j); - if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) { + if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) { m_layer_tools[j].has_wipe_tower = true; } else { LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index b35761b5f3..ea8465f225 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -698,7 +698,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str()) .append(";--------------------\n"); - writer.speed_override_backup(); + writer.speed_override_backup(); writer.speed_override(100); Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed); @@ -748,7 +748,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - ToolChangeResult result; + ToolChangeResult result; result.priming = false; result.initial_tool = int(old_tool); result.new_tool = int(m_current_tool); @@ -806,7 +806,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - ToolChangeResult result; + ToolChangeResult result; result.priming = false; result.initial_tool = int(old_tool); result.new_tool = int(m_current_tool); @@ -1163,7 +1163,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step); writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y()); } - writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle + writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle } else { // Extrude a sparse infill to support the material to be printed above. const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width); @@ -1196,7 +1196,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - ToolChangeResult result; + ToolChangeResult result; result.priming = false; result.initial_tool = int(old_tool); result.new_tool = int(m_current_tool); diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index cc8a86a96c..8cf4047a1a 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -309,49 +309,7 @@ convex_hull(const Polygons &polygons) return convex_hull(std::move(pp)); } -/* accepts an arrayref of points and returns a list of indices - according to a nearest-neighbor walk */ -void -chained_path(const Points &points, std::vector &retval, Point start_near) -{ - PointConstPtrs my_points; - std::map indices; - my_points.reserve(points.size()); - for (Points::const_iterator it = points.begin(); it != points.end(); ++it) { - my_points.push_back(&*it); - indices[&*it] = it - points.begin(); - } - - retval.reserve(points.size()); - while (!my_points.empty()) { - Points::size_type idx = start_near.nearest_point_index(my_points); - start_near = *my_points[idx]; - retval.push_back(indices[ my_points[idx] ]); - my_points.erase(my_points.begin() + idx); - } -} - -void -chained_path(const Points &points, std::vector &retval) -{ - if (points.empty()) return; // can't call front() on empty vector - chained_path(points, retval, points.front()); -} - -/* retval and items must be different containers */ -template -void -chained_path_items(Points &points, T &items, T &retval) -{ - std::vector indices; - chained_path(points, indices); - for (std::vector::const_iterator it = indices.begin(); it != indices.end(); ++it) - retval.push_back(items[*it]); -} -template void chained_path_items(Points &points, ClipperLib::PolyNodes &items, ClipperLib::PolyNodes &retval); - -bool -directions_parallel(double angle1, double angle2, double max_diff) +bool directions_parallel(double angle1, double angle2, double max_diff) { double diff = fabs(angle1 - angle2); max_diff += EPSILON; @@ -359,8 +317,7 @@ directions_parallel(double angle1, double angle2, double max_diff) } template -bool -contains(const std::vector &vector, const Point &point) +bool contains(const std::vector &vector, const Point &point) { for (typename std::vector::const_iterator it = vector.begin(); it != vector.end(); ++it) { if (it->contains(point)) return true; @@ -369,16 +326,14 @@ contains(const std::vector &vector, const Point &point) } template bool contains(const ExPolygons &vector, const Point &point); -double -rad2deg_dir(double angle) +double rad2deg_dir(double angle) { angle = (angle < PI) ? (-angle + PI/2.0) : (angle + PI/2.0); if (angle < 0) angle += PI; return rad2deg(angle); } -void -simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) +void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) { Polygons pp; for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { @@ -391,8 +346,7 @@ simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) *retval = Slic3r::simplify_polygons(pp); } -double -linint(double value, double oldmin, double oldmax, double newmin, double newmax) +double linint(double value, double oldmin, double oldmax, double newmin, double newmax) { return (value - oldmin) * (newmax - newmin) / (oldmax - oldmin) + newmin; } diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index eec2673227..a574209d81 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -138,9 +138,6 @@ Pointf3s convex_hull(Pointf3s points); Polygon convex_hull(Points points); Polygon convex_hull(const Polygons &polygons); -void chained_path(const Points &points, std::vector &retval, Point start_near); -void chained_path(const Points &points, std::vector &retval); -template void chained_path_items(Points &points, T &items, T &retval); bool directions_parallel(double angle1, double angle2, double max_diff = 0); template bool contains(const std::vector &vector, const Point &point); template T rad2deg(T angle) { return T(180.0) * angle / T(PI); } diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp new file mode 100644 index 0000000000..4f71dd3e4d --- /dev/null +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -0,0 +1,231 @@ +// KD tree built upon external data set, referencing the external data by integer indices. + +#ifndef slic3r_KDTreeIndirect_hpp_ +#define slic3r_KDTreeIndirect_hpp_ + +#include +#include +#include + +#include "Utils.hpp" // for next_highest_power_of_2() + +namespace Slic3r { + +// KD tree for N-dimensional closest point search. +template +class KDTreeIndirect +{ +public: + static constexpr size_t NumDimensions = ANumDimensions; + using CoordinateFn = ACoordinateFn; + using CoordType = ACoordType; + // 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)); } + KDTreeIndirect(CoordinateFn coordinate, std::vector &&indices) : coordinate(coordinate) { this->build(std::move(indices)); } + KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); } + KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {} + KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; } + void clear() { m_nodes.clear(); } + + void build(size_t num_indices) + { + std::vector indices; + indices.reserve(num_indices); + for (size_t i = 0; i < num_indices; ++ i) + indices.emplace_back(i); + this->build(std::move(indices)); + } + + void build(std::vector &&indices) + { + if (indices.empty()) + clear(); + else { + // Allocate a next highest power of 2 nodes, because the incomplete binary tree will not have the leaves filled strictly from the left. + m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos); + build_recursive(indices, 0, 0, 0, (int)(indices.size() - 1)); + } + indices.clear(); + } + + enum class VisitorReturnMask : unsigned int + { + CONTINUE_LEFT = 1, + CONTINUE_RIGHT = 2, + STOP = 4, + }; + template + unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const + { + CoordType dist = point_coord - this->coordinate(idx, dimension); + return (dist * dist < search_radius + CoordType(EPSILON)) ? + ((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) : + (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 + { + visit_recursive(0, 0, visitor); + } + + CoordinateFn coordinate; + +private: + // Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension. + void build_recursive(std::vector &input, size_t node, int dimension, int left, int right) + { + if (left > right) + return; + + assert(node < m_nodes.size()); + + if (left == right) { + // Insert a node into the balanced tree. + m_nodes[node] = input[left]; + return; + } + + // Partition the input sequence to two equal halves. + int center = (left + right) >> 1; + partition_input(input, dimension, left, right, center); + // Insert a node into the tree. + m_nodes[node] = input[center]; + // Partition the left and right subtrees. + size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension; + build_recursive(input, (node << 1) + 1, next_dimension, left, center - 1); + build_recursive(input, (node << 1) + 2, next_dimension, center + 1, right); + } + + // Partition the input m_nodes at k using QuickSelect method. + // https://en.wikipedia.org/wiki/Quickselect + void partition_input(std::vector &input, int dimension, int left, int right, int k) const + { + while (left < right) { + // Guess the k'th element. + // Pick the pivot as a median of first, center and last value. + // Sort first, center and last values. + int center = (left + right) >> 1; + auto left_value = this->coordinate(input[left], dimension); + auto center_value = this->coordinate(input[center], dimension); + auto right_value = this->coordinate(input[right], dimension); + if (center_value < left_value) { + std::swap(input[left], input[center]); + std::swap(left_value, center_value); + } + if (right_value < left_value) { + std::swap(input[left], input[right]); + std::swap(left_value, right_value); + } + if (right_value < center_value) { + std::swap(input[center], input[right]); + // No need to do that, result is not used. + // std::swap(center_value, right_value); + } + // Only two or three values are left and those are sorted already. + if (left + 3 > right) + break; + // left and right items are already at their correct positions. + // input[left].point[dimension] <= input[center].point[dimension] <= input[right].point[dimension] + // Move the pivot to the (right - 1) position. + std::swap(input[center], input[right - 1]); + // Pivot value. + double pivot = this->coordinate(input[right - 1], dimension); + // Partition the set based on the pivot. + int i = left; + int j = right - 1; + for (;;) { + // Skip left points that are already at correct positions. + // Search will certainly stop at position (right - 1), which stores the pivot. + while (this->coordinate(input[++ i], dimension) < pivot) ; + // Skip right points that are already at correct positions. + while (this->coordinate(input[-- j], dimension) > pivot && i < j) ; + if (i >= j) + break; + std::swap(input[i], input[j]); + } + // Restore pivot to the center of the sequence. + std::swap(input[i], input[right]); + // Which side the kth element is in? + if (k < i) + right = i - 1; + else if (k == i) + // Sequence is partitioned, kth element is at its place. + break; + else + left = i + 1; + } + } + + template + void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const + { + assert(! m_nodes.empty()); + if (node >= m_nodes.size() || m_nodes[node] == npos) + return; + + // Left / right child node index. + size_t left = (node << 1) + 1; + size_t right = left + 1; + unsigned int mask = visitor(m_nodes[node], dimension); + if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) { + size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension; + if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT) + visit_recursive(left, next_dimension, visitor); + if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT) + visit_recursive(right, next_dimension, visitor); + } + } + + std::vector m_nodes; +}; + +// Find a closest point using Euclidian metrics. +// Returns npos if not found. +template +size_t find_closest_point(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) +{ + struct Visitor { + using CoordType = typename KDTreeIndirectType::CoordType; + const KDTreeIndirectType &kdtree; + const PointType &point; + const FilterFn filter; + size_t min_idx = KDTreeIndirectType::npos; + CoordType min_dist = std::numeric_limits::max(); + + Visitor(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) : kdtree(kdtree), point(point), filter(filter) {} + unsigned int operator()(size_t idx, size_t dimension) { + if (this->filter(idx)) { + auto dist = CoordType(0); + for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++ i) { + CoordType d = point[i] - kdtree.coordinate(idx, i); + dist += d * d; + } + if (dist < min_dist) { + min_dist = dist; + min_idx = idx; + } + } + return kdtree.descent_mask(point[dimension], min_dist, idx, dimension); + } + } visitor(kdtree, point, filter); + + kdtree.visit(visitor); + return visitor.min_idx; +} + +template +size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& point) +{ + return find_closest_point(kdtree, point, [](size_t) { return true; }); +} + +} // namespace Slic3r + +#endif /* slic3r_KDTreeIndirect_hpp_ */ diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 4ac64b7775..94f114a261 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -1,8 +1,8 @@ #include "Layer.hpp" #include "ClipperUtils.hpp" -#include "Geometry.hpp" #include "Print.hpp" #include "Fill/Fill.hpp" +#include "ShortestPath.hpp" #include "SVG.hpp" #include @@ -57,8 +57,7 @@ void Layer::make_slices() ordering_points.push_back(ex.contour.first_point()); // sort slices - std::vector order; - Slic3r::Geometry::chained_path(ordering_points, order); + std::vector order = chain_points(ordering_points); // populate slices vector for (size_t i : order) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 6fc0b4e37f..bfe96d311c 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -144,7 +144,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly } if (! lower_layer_covered->empty()) voids = diff(voids, *lower_layer_covered); - fill_boundaries = diff(fill_boundaries, voids); + fill_boundaries = diff(fill_boundaries, voids); } } @@ -473,4 +473,4 @@ void LayerRegion::export_region_fill_surfaces_to_svg_debug(const char *name) con } } - \ No newline at end of file + diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 7526dd16aa..1e06f0703e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1462,7 +1462,7 @@ stl_stats ModelObject::get_object_stl_stats() const return this->volumes[0]->mesh().stl.stats; stl_stats full_stats; - memset(&full_stats, 0, sizeof(stl_stats)); + full_stats.volume = 0.f; // fill full_stats from all objet's meshes for (ModelVolume* volume : this->volumes) diff --git a/src/libslic3r/MutablePriorityQueue.hpp b/src/libslic3r/MutablePriorityQueue.hpp index 82e992fd64..da469b7ba2 100644 --- a/src/libslic3r/MutablePriorityQueue.hpp +++ b/src/libslic3r/MutablePriorityQueue.hpp @@ -13,21 +13,28 @@ public: {} ~MutablePriorityQueue() { clear(); } - inline void clear() { m_heap.clear(); } - inline void reserve(size_t cnt) { m_heap.reserve(cnt); } - inline void push(const T &item); - inline void push(T &&item); - inline void pop(); - inline T& top() { return m_heap.front(); } - inline void remove(size_t idx); - inline void update(size_t idx) { T item = m_heap[idx]; remove(idx); push(item); } + void clear(); + void reserve(size_t cnt) { m_heap.reserve(cnt); } + void push(const T &item); + void push(T &&item); + void pop(); + T& top() { return m_heap.front(); } + void remove(size_t idx); + void update(size_t idx) { T item = m_heap[idx]; remove(idx); push(item); } - inline size_t size() const { return m_heap.size(); } - inline bool empty() const { return m_heap.empty(); } + size_t size() const { return m_heap.size(); } + bool empty() const { return m_heap.empty(); } + + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + iterator begin() { return m_heap.begin(); } + iterator end() { return m_heap.end(); } + const_iterator cbegin() const { return m_heap.cbegin(); } + const_iterator cend() const { return m_heap.cend(); } protected: - inline void update_heap_up(size_t top, size_t bottom); - inline void update_heap_down(size_t top, size_t bottom); + void update_heap_up(size_t top, size_t bottom); + void update_heap_down(size_t top, size_t bottom); private: std::vector m_heap; @@ -42,6 +49,17 @@ MutablePriorityQueue make_mutable_priority_queue( std::forward(index_setter), std::forward(less_predicate)); } +template +inline void MutablePriorityQueue::clear() +{ +#ifndef NDEBUG + for (size_t idx = 0; idx < m_heap.size(); ++ idx) + // Mark as removed from the queue. + m_index_setter(m_heap[idx], std::numeric_limits::max()); +#endif /* NDEBUG */ + m_heap.clear(); +} + template inline void MutablePriorityQueue::push(const T &item) { @@ -64,6 +82,10 @@ template inline void MutablePriorityQueue::pop() { assert(! m_heap.empty()); +#ifndef NDEBUG + // Mark as removed from the queue. + m_index_setter(m_heap.front(), std::numeric_limits::max()); +#endif /* NDEBUG */ if (m_heap.size() > 1) { m_heap.front() = m_heap.back(); m_heap.pop_back(); @@ -77,6 +99,10 @@ template inline void MutablePriorityQueue::remove(size_t idx) { assert(idx < m_heap.size()); +#ifndef NDEBUG + // Mark as removed from the queue. + m_index_setter(m_heap[idx], std::numeric_limits::max()); +#endif /* NDEBUG */ if (idx + 1 == m_heap.size()) { m_heap.pop_back(); return; diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 0c16f4a1d4..cd53547769 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1,6 +1,8 @@ #include "PerimeterGenerator.hpp" #include "ClipperUtils.hpp" #include "ExtrusionEntityCollection.hpp" +#include "ShortestPath.hpp" + #include #include @@ -86,24 +88,24 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi return paths; } -static ExtrusionEntityCollection variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow) +static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow, std::vector &out) { // This value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount // of segments, and any pruning shall be performed before we apply this tolerance. - ExtrusionEntityCollection coll; const float tolerance = float(scale_(0.05)); for (const ThickPolyline &p : polylines) { ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance); // Append paths to collection. if (! paths.empty()) { if (paths.front().first_point() == paths.back().last_point()) - coll.append(ExtrusionLoop(std::move(paths))); - else - coll.append(std::move(paths)); + out.emplace_back(new ExtrusionLoop(std::move(paths))); + else { + for (ExtrusionPath &path : paths) + out.emplace_back(new ExtrusionPath(std::move(path))); + } } } - return coll; } // Hierarchy of perimeters. @@ -186,43 +188,47 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime paths.push_back(path); } - coll.append(ExtrusionLoop(paths, loop_role)); + coll.append(ExtrusionLoop(std::move(paths), loop_role)); } // Append thin walls to the nearest-neighbor search (only for first iteration) if (! thin_walls.empty()) { - ExtrusionEntityCollection tw = variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow); - coll.append(tw.entities); + variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow, coll.entities); thin_walls.clear(); } - // Sort entities into a new collection using a nearest-neighbor search, - // preserving the original indices which are useful for detecting thin walls. - ExtrusionEntityCollection sorted_coll; - coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices); - - // traverse children and build the final collection - ExtrusionEntityCollection entities; - for (const size_t &idx : sorted_coll.orig_indices) { - if (idx >= loops.size()) { - // This is a thin wall. Let's get it from the sorted collection as it might have been reversed. - entities.append(std::move(*sorted_coll.entities[&idx - &sorted_coll.orig_indices.front()])); + // Traverse children and build the final collection. + Point zero_point(0, 0); + std::vector> chain = chain_extrusion_entities(coll.entities, &zero_point); + ExtrusionEntityCollection out; + for (const std::pair &idx : chain) { + assert(coll.entities[idx.first] != nullptr); + if (idx.first >= loops.size()) { + // This is a thin wall. + out.entities.reserve(out.entities.size() + 1); + out.entities.emplace_back(coll.entities[idx.first]); + coll.entities[idx.first] = nullptr; + if (idx.second) + out.entities.back()->reverse(); } else { - const PerimeterGeneratorLoop &loop = loops[idx]; - ExtrusionLoop eloop = *dynamic_cast(coll.entities[idx]); + const PerimeterGeneratorLoop &loop = loops[idx.first]; + assert(thin_walls.empty()); ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls); + out.entities.reserve(out.entities.size() + children.entities.size() + 1); + ExtrusionLoop *eloop = static_cast(coll.entities[idx.first]); + coll.entities[idx.first] = nullptr; if (loop.is_contour) { - eloop.make_counter_clockwise(); - entities.append(std::move(children.entities)); - entities.append(std::move(eloop)); + eloop->make_counter_clockwise(); + out.append(std::move(children.entities)); + out.entities.emplace_back(eloop); } else { - eloop.make_clockwise(); - entities.append(std::move(eloop)); - entities.append(std::move(children.entities)); + eloop->make_clockwise(); + out.entities.emplace_back(eloop); + out.append(std::move(children.entities)); } } } - return entities; + return out; } void PerimeterGenerator::process() @@ -445,8 +451,8 @@ void PerimeterGenerator::process() for (const ExPolygon &ex : gaps_ex) ex.medial_axis(max, min, &polylines); if (! polylines.empty()) { - ExtrusionEntityCollection gap_fill = variable_width(polylines, erGapFill, this->solid_infill_flow); - this->gap_fill->append(gap_fill.entities); + ExtrusionEntityCollection gap_fill; + variable_width(polylines, erGapFill, this->solid_infill_flow, gap_fill.entities); /* Make sure we don't infill narrow parts that are already gap-filled (we only consider this surface's gaps to reduce the diff() complexity). Growing actual extrusions ensures that gaps not filled by medial axis @@ -456,7 +462,8 @@ void PerimeterGenerator::process() //FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing, // therefore it may cover the area, but no the volume. last = diff_ex(to_polygons(last), gap_fill.polygons_covered_by_width(10.f)); - } + this->gap_fill->append(std::move(gap_fill.entities)); + } } // create one more offset to be used as boundary for fill diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index af155468ab..531383ae14 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -23,12 +23,6 @@ Polyline::operator Line() const return Line(this->points.front(), this->points.back()); } -Point -Polyline::last_point() const -{ - return this->points.back(); -} - Point Polyline::leftmost_point() const { diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index e17fafd622..d595f252d6 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -62,7 +62,8 @@ public: operator Polylines() const; operator Line() const; - Point last_point() const; + Point last_point() const override { return this->points.back(); } + Point leftmost_point() const; virtual Lines lines() const; void clip_end(double distance); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4d84827434..f5601276f0 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -7,6 +7,7 @@ #include "Flow.hpp" #include "Geometry.hpp" #include "I18N.hpp" +#include "ShortestPath.hpp" #include "SupportMaterial.hpp" #include "GCode.hpp" #include "GCode/WipeTower.hpp" @@ -252,7 +253,7 @@ bool Print::is_step_done(PrintObjectStep step) const { if (m_objects.empty()) return false; - tbb::mutex::scoped_lock lock(this->state_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); for (const PrintObject *object : m_objects) if (! object->is_step_done_unguarded(step)) return false; @@ -1824,8 +1825,8 @@ void Print::_make_brim() [](const std::pair &l, const std::pair &r) { return l.second < r.second; }); - Vec3f last_pt(0.f, 0.f, 0.f); + Point last_pt(0, 0); for (size_t i = 0; i < loops_trimmed_order.size();) { // Find all pieces that the initial loop was split into. size_t j = i + 1; @@ -1841,16 +1842,23 @@ void Print::_make_brim() points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); i = j; } else { - //FIXME this is not optimal as the G-code generator will follow the sequence of paths verbatim without respect to minimum travel distance. + //FIXME The path chaining here may not be optimal. + ExtrusionEntityCollection this_loop_trimmed; + this_loop_trimmed.entities.reserve(j - i); for (; i < j; ++ i) { - m_brim.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()))); + this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()))); const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; - Points &points = static_cast(m_brim.entities.back())->polyline.points; + Points &points = static_cast(this_loop_trimmed.entities.back())->polyline.points; points.reserve(path.size()); for (const ClipperLib_Z::IntPoint &pt : path) points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); } + chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt); + m_brim.entities.reserve(m_brim.entities.size() + this_loop_trimmed.entities.size()); + append(m_brim.entities, std::move(this_loop_trimmed.entities)); + this_loop_trimmed.entities.clear(); } + last_pt = m_brim.last_point(); } } } else { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 89a5f3e745..5cb13039c2 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -96,6 +96,7 @@ public: const SupportLayerPtrs& support_layers() const { return m_support_layers; } const Transform3d& trafo() const { return m_trafo; } const Points& copies() const { return m_copies; } + const Point copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); } // since the object is aligned to origin, bounding box coincides with size BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index aebc879044..05d884cc88 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -268,8 +268,7 @@ public: std::string text; // Bitmap of flags. enum FlagBits { - DEFAULT, - NO_RELOAD_SCENE = 0, + DEFAULT = 0, RELOAD_SCENE = 1 << 1, RELOAD_SLA_SUPPORT_POINTS = 1 << 2, RELOAD_SLA_PREVIEW = 1 << 3, diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2834d91051..c4f77b6d7e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -75,13 +74,9 @@ PrintBase::ApplyStatus PrintObject::set_copies(const Points &points) { // Order copies with a nearest-neighbor search. std::vector copies; - { - std::vector ordered_copies; - Slic3r::Geometry::chained_path(points, ordered_copies); - copies.reserve(ordered_copies.size()); - for (size_t point_idx : ordered_copies) - copies.emplace_back(points[point_idx] + m_copies_shift); - } + copies.reserve(points.size()); + for (const Point &pt : points) + copies.emplace_back(pt + m_copies_shift); // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; if (copies != m_copies) { @@ -1480,7 +1475,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full if (object_max_z <= 0.f) object_max_z = (float)model_object.raw_bounding_box().size().z(); - return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders); + return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders); } // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp new file mode 100644 index 0000000000..490bcf1dcc --- /dev/null +++ b/src/libslic3r/ShortestPath.cpp @@ -0,0 +1,394 @@ +#if 0 + #pragma optimize("", off) + #undef NDEBUG + #undef assert +#endif + +#include "clipper.hpp" +#include "ShortestPath.hpp" +#include "KDTreeIndirect.hpp" +#include "MutablePriorityQueue.hpp" +#include "Print.hpp" + +#include +#include + +namespace Slic3r { + +// 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. +// Return index and "reversed" flag. +// https://en.wikipedia.org/wiki/Multi-fragment_algorithm +// 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) +{ + std::vector> out; + + if (num_segments == 0) { + // Nothing to do. + } + else if (num_segments == 1) + { + // 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).template cast().squaredNorm() < (end_point_func(0, false) - *start_near).template cast().squaredNorm()); + } + else + { + // End points of segments for the KD tree closest point search. + // A single end point is inserted into the search structure for loops, two end points are entered for open paths. + struct EndPoint { + EndPoint(const Vec2d &pos) : pos(pos) {} + Vec2d pos; + // Identifier of the chain, to which this end point belongs. Zero means unassigned. + size_t chain_id = 0; + // Link to the closest currently valid end point. + EndPoint *edge_out = nullptr; + // Distance to the next end point following the link. + // Zero value -> start of the final path. + double distance_out = std::numeric_limits::max(); + size_t heap_idx = std::numeric_limits::max(); + }; + 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 ).template cast()); + end_points.emplace_back(end_point_func(i, false).template cast()); + } + + // Construct the closest point KD tree over end points of segments. + auto coordinate_fn = [&end_points](size_t idx, size_t dimension) -> double { return end_points[idx].pos[dimension]; }; + KDTreeIndirect<2, double, decltype(coordinate_fn)> kdtree(coordinate_fn, end_points.size()); + + // Helper to detect loops in already connected paths. + // Unique chain IDs are assigned to paths. If paths are connected, end points will not have their chain IDs updated, but the chain IDs + // will remember an "equivalent" chain ID, which is the lowest ID of all the IDs in the path, and the lowest ID is equivalent to itself. + class EquivalentChains { + public: + // Zero'th chain ID is invalid. + EquivalentChains(size_t reserve) { m_equivalent_with.reserve(reserve); m_equivalent_with.emplace_back(0); } + // Generate next equivalence class. + size_t next() { + m_equivalent_with.emplace_back(++ m_last_chain_id); + return m_last_chain_id; + } + // Get equivalence class for chain ID. + size_t operator()(size_t chain_id) { + if (chain_id != 0) { + for (size_t last = chain_id;;) { + size_t lower = m_equivalent_with[last]; + if (lower == last) { + m_equivalent_with[chain_id] = lower; + chain_id = lower; + break; + } + last = lower; + } + } + return chain_id; + } + size_t merge(size_t chain_id1, size_t chain_id2) { + size_t chain_id = std::min((*this)(chain_id1), (*this)(chain_id2)); + m_equivalent_with[chain_id1] = chain_id; + m_equivalent_with[chain_id2] = chain_id; + return chain_id; + } + +#ifndef NDEBUG + bool validate() + { + assert(m_last_chain_id >= 0); + assert(m_last_chain_id + 1 == m_equivalent_with.size()); + for (size_t i = 0; i < m_equivalent_with.size(); ++ i) { + for (size_t last = i;;) { + size_t lower = m_equivalent_with[last]; + assert(lower <= last); + if (lower == last) + break; + last = lower; + } + } + return true; + } +#endif /* NDEBUG */ + + private: + // Unique chain ID assigned to chains of end points of segments. + size_t m_last_chain_id = 0; + std::vector m_equivalent_with; + } equivalent_chain(num_segments); + + // Find the first end point closest to start_near. + 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->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; + } + + // Assign the closest point and distance to the end points. + for (EndPoint &end_point : end_points) { + assert(end_point.edge_out == nullptr); + if (&end_point != first_point) { + size_t this_idx = &end_point - &end_points.front(); + // 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, end_point.pos, + [this_idx, first_point_idx](size_t idx){ return idx != first_point_idx && (idx ^ this_idx) > 1; }); + assert(next_idx < end_points.size()); + EndPoint &end_point2 = end_points[next_idx]; + end_point.edge_out = &end_point2; + end_point.distance_out = (end_point2.pos - end_point.pos).squaredNorm(); + } + } + + // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path. + auto queue = make_mutable_priority_queue( + [](EndPoint *ep, size_t idx){ ep->heap_idx = idx; }, + [](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; }); + queue.reserve(end_points.size() * 2 - 1); + for (EndPoint &ep : end_points) + if (first_point != &ep) + queue.push(&ep); + +#ifndef NDEBUG + auto validate_graph_and_queue = [&equivalent_chain, &end_points, &queue, first_point]() -> bool { + assert(equivalent_chain.validate()); + for (EndPoint &ep : end_points) { + if (ep.heap_idx < queue.size()) { + // End point is on the heap. + assert(*(queue.cbegin() + ep.heap_idx) == &ep); + assert(ep.chain_id == 0); + } else { + // End point is NOT on the heap, therefore it is part of the output path. + assert(ep.heap_idx == std::numeric_limits::max()); + assert(ep.chain_id != 0); + if (&ep == first_point) { + assert(ep.edge_out == nullptr); + } else { + assert(ep.edge_out != nullptr); + // Detect loops. + for (EndPoint *pt = &ep; pt != nullptr;) { + // Out of queue. It is a final point. + assert(pt->heap_idx == std::numeric_limits::max()); + EndPoint *pt_other = &end_points[(pt - &end_points.front()) ^ 1]; + if (pt_other->heap_idx < queue.size()) + // The other side of this segment is undecided yet. + break; + pt = pt_other->edge_out; + } + } + } + } + for (EndPoint *ep : queue) + // Points in the queue are not connected yet. + assert(ep->chain_id == 0); + return true; + }; +#endif /* NDEBUG */ + + // Chain the end points: find (num_segments - 1) shortest links not forming bifurcations or loops. + assert(num_segments >= 2); + for (int iter = int(num_segments) - 2;; -- iter) { + assert(validate_graph_and_queue()); + // Take the first end point, for which the link points to the currently closest valid neighbor. + EndPoint &end_point1 = *queue.top(); + assert(end_point1.edge_out != nullptr); + // No point on the queue may be connected yet. + assert(end_point1.chain_id == 0); + // Take the closest end point to the first end point, + EndPoint &end_point2 = *end_point1.edge_out; + bool valid = true; + size_t end_point1_other_chain_id = 0; + size_t end_point2_other_chain_id = 0; + if (end_point2.chain_id > 0) { + // The other side is part of the output path. Don't connect to end_point2, update end_point1 and try another one. + valid = false; + } else { + // End points of the opposite ends of the segments. + end_point1_other_chain_id = equivalent_chain(end_points[(&end_point1 - &end_points.front()) ^ 1].chain_id); + end_point2_other_chain_id = equivalent_chain(end_points[(&end_point2 - &end_points.front()) ^ 1].chain_id); + if (end_point1_other_chain_id == end_point2_other_chain_id && end_point1_other_chain_id != 0) + // This edge forms a loop. Update end_point1 and try another one. + valid = false; + } + if (valid) { + // Remove the first and second point from the queue. + queue.pop(); + queue.remove(end_point2.heap_idx); + assert(end_point1.edge_out = &end_point2); + end_point2.edge_out = &end_point1; + end_point2.distance_out = end_point1.distance_out; + // Assign chain IDs to the newly connected end points, set equivalent_chain if two chains were merged. + size_t chain_id = + (end_point1_other_chain_id == 0) ? + ((end_point2_other_chain_id == 0) ? equivalent_chain.next() : end_point2_other_chain_id) : + ((end_point2_other_chain_id == 0) ? end_point1_other_chain_id : + (end_point1_other_chain_id == end_point2_other_chain_id) ? + end_point1_other_chain_id : + equivalent_chain.merge(end_point1_other_chain_id, end_point2_other_chain_id)); + end_point1.chain_id = chain_id; + end_point2.chain_id = chain_id; + assert(validate_graph_and_queue()); + 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) + first_point = queue.top(); + while (! queue.empty()) { + queue.top()->edge_out = nullptr; + queue.pop(); + } + break; + } + } else { + // This edge forms a loop. Update end_point1 and try another one. + ++ iter; + end_point1.edge_out = nullptr; + // Update edge_out and distance. + size_t this_idx = &end_point1 - &end_points.front(); + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the filter lambda). + size_t next_idx = find_closest_point(kdtree, end_point1.pos, [&end_points, &equivalent_chain, this_idx](size_t idx) { + assert(end_points[this_idx].edge_out == nullptr); + assert(end_points[this_idx].chain_id == 0); + if ((idx ^ this_idx) <= 1 || end_points[idx].chain_id != 0) + // Points of the same segment shall not be connected, + // cannot connect to an already connected point (ideally those would be removed from the KD tree, but the update is difficult). + return false; + size_t chain1 = equivalent_chain(end_points[this_idx ^ 1].chain_id); + size_t chain2 = equivalent_chain(end_points[idx ^ 1].chain_id); + return chain1 != chain2 || chain1 == 0; + }); + assert(next_idx < end_points.size()); + end_point1.edge_out = &end_points[next_idx]; + end_point1.distance_out = (end_points[next_idx].pos - end_point1.pos).squaredNorm(); + // Update position of this end point in the queue based on the distance calculated at the line above. + queue.update(end_point1.heap_idx); + //FIXME Remove the other end point from the KD tree. + // As the KD tree update is expensive, do it only after some larger number of points is removed from the queue. + assert(validate_graph_and_queue()); + } + } + assert(queue.empty()); + + // Now interconnect pairs of segments into a chain. + assert(first_point != nullptr); + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + out.emplace_back(segment_id, (first_point_id & 1) != 0); + first_point = second_point->edge_out; + } while (first_point != nullptr); + } + + assert(out.size() == num_segments); + return out; +} + +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); + for (size_t i = 0; i < entities.size(); ++ i) { + ExtrusionEntity *ee = entities[i]; + if (ee->is_loop()) + // Ignore reversals for loops, as the start point equals the end point. + out[i].second = false; + // Is can_reverse() respected by the reversals? + assert(entities[i]->can_reverse() || ! out[i].second); + } + return out; +} + +void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain) +{ + assert(entities.size() == chain.size()); + std::vector out; + out.reserve(entities.size()); + for (const std::pair &idx : chain) { + assert(entities[idx.first] != nullptr); + out.emplace_back(entities[idx.first]); + if (idx.second) + out.back()->reverse(); + } + entities.swap(out); +} + +void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near) +{ + reorder_extrusion_entities(entities, chain_extrusion_entities(entities, 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 out; + out.reserve(ordered.size()); + for (auto &segment_and_reversal : ordered) + out.emplace_back(segment_and_reversal.first); + return out; +} + +Polylines chain_infill_polylines(Polylines &&polylines) +{ + 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); + Polylines out; + 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) + out.back().reverse(); + } + return out; +} + +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); + T out; + out.reserve(items.size()); + for (auto &segment_and_reversal : ordered) + out.emplace_back(items[segment_and_reversal.first]); + return out; +} + +ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items) +{ + return chain_path_items(points, items); +} + +std::vector> chain_print_object_instances(const Print &print) +{ + // Order objects using a nearest neighbor search. + Points object_reference_points; + std::vector> instances; + for (size_t i = 0; i < print.objects().size(); ++ i) { + const PrintObject &object = *print.objects()[i]; + for (size_t j = 0; j < object.copies().size(); ++ j) { + object_reference_points.emplace_back(object.copy_center(j)); + instances.emplace_back(i, j); + } + } + 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> out; + out.reserve(instances.size()); + for (auto &segment_and_reversal : ordered) + out.emplace_back(instances[segment_and_reversal.first]); + return out; +} + +} // namespace Slic3r diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp new file mode 100644 index 0000000000..06081e1fc0 --- /dev/null +++ b/src/libslic3r/ShortestPath.hpp @@ -0,0 +1,33 @@ +#ifndef slic3r_ShortestPath_hpp_ +#define slic3r_ShortestPath_hpp_ + +#include "libslic3r.h" +#include "ExtrusionEntity.hpp" +#include "Point.hpp" + +#include +#include + +namespace ClipperLib { class PolyNode; } + +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, 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_clipper_polynodes(const Points &points, const std::vector &items); + +// Chain instances of print objects by an approximate shortest path. +// Returns pairs of PrintObject idx and instance of that PrintObject. +class Print; +std::vector> chain_print_object_instances(const Print &print); + + +} // namespace Slic3r + +#endif /* slic3r_ShortestPath_hpp_ */ diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 505fdcaf58..c0c8e9ea0a 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -783,7 +783,7 @@ namespace SupportMaterialInternal { for (const ExtrusionPath &ep : loop.paths) if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) return ep.size() >= (ep.is_closed() ? 3 : 2); - return false; + return false; } static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters) { @@ -2125,7 +2125,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( } // $layer->slices contains the full shape of layer, thus including // perimeter's width. $support contains the full shape of support - // material, thus including the width of its foremost extrusion. + // material, thus including the width of its foremost extrusion. // We leave a gap equal to a full extrusion width. support_layer.polygons = diff(support_layer.polygons, polygons_trimming); } @@ -2934,20 +2934,13 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Prepare fillers. SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; bool with_sheath = m_object_config->support_material_with_sheath; - InfillPattern infill_pattern; + InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipRectilinear); std::vector angles; angles.push_back(base_angle); - switch (support_pattern) { - case smpRectilinearGrid: + + if (support_pattern == smpRectilinearGrid) angles.push_back(interface_angle); - // fall through - case smpRectilinear: - infill_pattern = ipRectilinear; - break; - case smpHoneycomb: - infill_pattern = ipHoneycomb; - break; - } + BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); // const coordf_t link_max_length_factor = 3.; @@ -3217,7 +3210,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( density = 0.5f; flow = m_first_layer_flow; // use the proper spacing for first layer as we don't need to align - // its pattern to the other layers + // its pattern to the other layers //FIXME When paralellizing, each thread shall have its own copy of the fillers. filler->spacing = flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 622b31a178..80b6521b6f 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -342,7 +342,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src ! boost::filesystem::create_directory(path_dst)) throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); - for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) + for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) if (Slic3r::is_ini_file(dir_entry)) boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists); } @@ -351,7 +351,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path) { if (! boost::filesystem::is_directory(path)) return; - for (auto &dir_entry : boost::filesystem::directory_iterator(path)) + for (auto &dir_entry : boost::filesystem::directory_iterator(path)) if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) boost::filesystem::remove(dir_entry.path()); } @@ -378,7 +378,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: sprintf(name, "filament_%u", i); if (! app_config.has("presets", name)) break; - snapshot.filaments.emplace_back(app_config.get("presets", name)); + snapshot.filaments.emplace_back(app_config.get("presets", name)); } // Vendor specific config bundles and installed printers. for (const std::pair>> &vendor : app_config.vendors()) { @@ -417,7 +417,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: // Backup the presets. for (const char *subdir : { "print", "filament", "printer", "vendor" }) copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); - snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); + snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured); m_snapshots.emplace_back(std::move(snapshot)); return m_snapshots.back(); diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 175abff69a..3f8f960f10 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -227,9 +227,9 @@ size_t Index::load(const boost::filesystem::path &path) // End of semver or keyword. break; } - if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=') + if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=') throw file_parser_error("Invalid keyword or semantic version", path, idx_line); - char *value = left_trim(key_end); + char *value = left_trim(key_end); bool key_value_pair = *value == '='; if (key_value_pair) value = left_trim(value + 1); @@ -245,11 +245,11 @@ size_t Index::load(const boost::filesystem::path &path) if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) { if (! svalue.empty()) semver = Semver::parse(svalue); - if (! semver) + if (! semver) throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line); - if (strcmp(key, "min_slic3r_version") == 0) + if (strcmp(key, "min_slic3r_version") == 0) ver.min_slic3r_version = *semver; - else + else ver.max_slic3r_version = *semver; } else { // Ignore unknown keys, as there may come new keys in the future. diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 90fc0ff80b..5624ada9d2 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -455,7 +455,7 @@ void BedShapePanel::update_shape() else if (page_idx == SHAPE_CUSTOM) m_shape = m_loaded_shape; - update_preview(); + update_preview(); } // Loads an stl file, projects it to the XY plane and calculates a polygon. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2960d08c3f..53d69262b7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -931,8 +931,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c if (items_count > 1) m_original_height += (items_count - 1) * scaled_square_contour; - m_width = (int)next_highest_power_of_2((uint32_t)m_original_width); - m_height = (int)next_highest_power_of_2((uint32_t)m_original_height); + m_width = (int)next_highest_power_of_2((uint32_t)m_original_width); + m_height = (int)next_highest_power_of_2((uint32_t)m_original_height); // generates bitmap wxBitmap bitmap(m_width, m_height); @@ -1885,7 +1885,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (m_reload_delayed) return; - bool update_object_list = false; + bool update_object_list = false; if (m_volumes.volumes != glvolumes_new) update_object_list = true; @@ -3413,10 +3413,9 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc m_sidebar_field = focus_on ? opt_key : ""; if (!m_sidebar_field.empty()) - { m_gizmos.reset_all_states(); - m_dirty = true; - } + + m_dirty = true; } void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type) @@ -5096,14 +5095,14 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers) for (const ExtrusionPath &path : layer.paths) ++ num_paths_per_role[size_t(path.role())]; - std::vector> roles_values; + 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 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)); - roles_filters.reserve(size_t(erCount)); + roles_filters.reserve(size_t(erCount)); size_t num_buffers = 0; for (std::vector &values : roles_values) { sort_remove_duplicates(values); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index f94372667c..6e8c361c82 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -125,7 +125,7 @@ void config_wizard(int reason) if (! wxGetApp().check_unsaved_changes()) return; - try { + try { ConfigWizard wizard(nullptr, static_cast(reason)); wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 80c02ea78e..86523cb88a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -725,7 +725,7 @@ bool GUI_App::load_language(wxString language, bool initial) #endif if (initial) message + "\n\nApplication will close."; - wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR); + wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR); if (initial) std::exit(EXIT_FAILURE); else diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 573fbe9801..19dedc7b0e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -753,9 +753,9 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol } select_items(items); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::paste_objects_into_list(const std::vector& object_idxs) @@ -773,9 +773,9 @@ void ObjectList::paste_objects_into_list(const std::vector& object_idxs) wxGetApp().plater()->changed_objects(object_idxs); select_items(items); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } #ifdef __WXOSX__ @@ -815,7 +815,9 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) if (col == nullptr) { if (wxOSX) UnselectAll(); - else + else if (!evt_context_menu) + // Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED, + // which invoked next list_manipulation(false) return; } @@ -1720,9 +1722,9 @@ void ObjectList::load_subobject(ModelVolumeType type) if (sel_item) select_item(sel_item); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::load_part( ModelObject* model_object, @@ -1858,9 +1860,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode const auto object_item = m_objects_model->GetTopParent(GetSelection()); select_item(m_objects_model->AddVolumeChild(object_item, name, type, new_volume->get_mesh_errors_count()>0)); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME +//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ +//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::load_shape_object(const std::string& type_name) @@ -1899,6 +1901,9 @@ void ObjectList::load_shape_object(const std::string& type_name) new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); new_object->invalidate_bounding_box(); + new_object->center_around_origin(); + new_object->ensure_on_bed(); + const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb(); new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -new_object->origin_translation(2))); diff --git a/src/slic3r/GUI/LambdaObjectDialog.cpp b/src/slic3r/GUI/LambdaObjectDialog.cpp index 4d1cb06584..63c8d329c5 100644 --- a/src/slic3r/GUI/LambdaObjectDialog.cpp +++ b/src/slic3r/GUI/LambdaObjectDialog.cpp @@ -192,7 +192,7 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wx else panel->SetSizer(optgroup->sizer); - return optgroup; + return optgroup; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ae0413937e..615d7d8a96 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -924,7 +924,7 @@ void MainFrame::load_config_file() wxString file; if (dlg.ShowModal() == wxID_OK) file = dlg.GetPath(); - if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { + if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { wxGetApp().app_config->update_config_dir(get_dir_name(file)); m_last_config = file; } diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index c032aac729..698c1e0348 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -133,7 +133,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n m_options_mode.push_back(option_set[0].opt.mode); // if we have a single option with no label, no sidetext just add it directly to sizer - if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && + if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && option_set.front().opt.label.empty() && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b721ee8fdc..84618c9113 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3346,7 +3346,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) this->statusbar()->set_progress(evt.status.percent); this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…")); } - if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) { + if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) { switch (this->printer_technology) { case ptFFF: this->update_fff_scene(); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index d9e90333c6..5785fd8507 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -279,7 +279,7 @@ std::string PresetBundle::load_system_presets() errors_cummulative += "\n"; } } - if (first) { + if (first) { // No config bundle loaded, reset. this->reset(false); } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 40fbbbac63..13d4a73602 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -410,7 +410,7 @@ void Selection::set_deserialized(EMode mode, const std::vectorselected = false; m_list.clear(); diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 460683f772..5394225456 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -371,7 +371,7 @@ void WipingPanel::toggle_advanced(bool user_action) { else m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate - (m_advanced ? m_page_advanced : m_page_simple)->Show(); + (m_advanced ? m_page_advanced : m_page_simple)->Show(); (!m_advanced ? m_page_advanced : m_page_simple)->Hide(); m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings"))); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index bf5500cf45..59406b6e97 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -669,7 +669,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name, if (has_errors) root->m_bmp = *m_warning_bmp; - m_objects.push_back(root); + m_objects.push_back(root); // notify control wxDataViewItem child((void*)root); wxDataViewItem parent((void*)NULL); @@ -720,7 +720,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent root->SetBitmap(*m_warning_bmp); // notify control - const wxDataViewItem child((void*)node); + const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); root->m_volumes_cnt++; diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index b7e92ba695..5d6454e8a8 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -3,6 +3,7 @@ %{ #include #include "libslic3r/Geometry.hpp" +#include "libslic3r/ShortestPath.hpp" %} @@ -49,7 +50,7 @@ std::vector chained_path(points) Points points CODE: - Slic3r::Geometry::chained_path(points, RETVAL); + RETVAL = chain_points(points); OUTPUT: RETVAL @@ -58,7 +59,7 @@ chained_path_from(points, start_from) Points points Point* start_from CODE: - Slic3r::Geometry::chained_path(points, RETVAL, *start_from); + RETVAL = chain_points(points, start_from); OUTPUT: RETVAL