From 82a52e8b2168740327d025d5f326077472e6fe00 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 23 Sep 2019 14:56:27 +0200 Subject: [PATCH 01/10] #2922 - Port of commit: https://github.com/prusa3d/PrusaSlicer/pull/2922/commits/f42edd35ce9e438de2c5fcacb69bfa66d6a28bbc by jschuh --- src/libslic3r/ExtrusionEntity.hpp | 18 +++++---- src/libslic3r/GCode/Analyzer.cpp | 59 ++++++++++++++++++++++++++--- src/libslic3r/GCode/Analyzer.hpp | 14 ++++++- src/libslic3r/GCode/PreviewData.cpp | 13 +++++++ src/libslic3r/GCode/PreviewData.hpp | 4 ++ src/slic3r/GUI/GLCanvas3D.cpp | 4 ++ src/slic3r/GUI/GUI_Preview.cpp | 1 + 7 files changed, 98 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index ce52ae1526..3c6bde47a5 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -121,17 +121,19 @@ public: unsigned int extruder_id; // Id of the color, used for visualization purposes in the color printing case. unsigned int cp_color_id; + // Fan speed for the extrusion, used for visualization purposes. + float fan_speed; - ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} - ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} - ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} + ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), fan_speed(0.0f), m_role(role) {}; + ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), fan_speed(0.0f), m_role(role) {}; + ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {} + ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {} + ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {} + ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), fan_speed(rhs.fan_speed), m_role(rhs.m_role) {} // ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {}; - ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; } - ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; } + ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->fan_speed = rhs.fan_speed, this->polyline = rhs.polyline; return *this; } + ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->fan_speed = rhs.fan_speed, this->polyline = std::move(rhs.polyline); return *this; } ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); } // Create a new object, initialize it with this object using the move semantics. diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 20f0483b0e..fa4414da9a 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -20,6 +20,7 @@ static const unsigned int DEFAULT_EXTRUDER_ID = 0; static const unsigned int DEFAULT_COLOR_PRINT_ID = 0; static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f); static const float DEFAULT_START_EXTRUSION = 0.0f; +static const float DEFAULT_FAN_SPEED = 0.0f; namespace Slic3r { @@ -36,21 +37,23 @@ const float GCodeAnalyzer::Default_Height = 0.0f; GCodeAnalyzer::Metadata::Metadata() : extrusion_role(erNone) , extruder_id(DEFAULT_EXTRUDER_ID) - , cp_color_id(DEFAULT_COLOR_PRINT_ID) , mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm) , width(GCodeAnalyzer::Default_Width) , height(GCodeAnalyzer::Default_Height) , feedrate(DEFAULT_FEEDRATE) + , fan_speed(DEFAULT_FAN_SPEED) + , cp_color_id(DEFAULT_COLOR_PRINT_ID) { } -GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id/* = 0*/) +GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id/* = 0*/) : extrusion_role(extrusion_role) , extruder_id(extruder_id) , mm3_per_mm(mm3_per_mm) , width(width) , height(height) , feedrate(feedrate) + , fan_speed(fan_speed) , cp_color_id(cp_color_id) { } @@ -75,15 +78,18 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other) if (feedrate != other.feedrate) return true; + if (fan_speed != other.fan_speed) + return true; + if (cp_color_id != other.cp_color_id) return true; return false; } -GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id/* = 0*/) +GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id/* = 0*/) : type(type) - , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, cp_color_id) + , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, fan_speed, cp_color_id) , start_position(start_position) , end_position(end_position) , delta_extruder(delta_extruder) @@ -133,6 +139,7 @@ void GCodeAnalyzer::reset() _set_feedrate(DEFAULT_FEEDRATE); _set_start_position(DEFAULT_START_POSITION); _set_start_extrusion(DEFAULT_START_EXTRUSION); + _set_fan_speed(DEFAULT_FAN_SPEED); _reset_axes_position(); _reset_cached_position(); @@ -259,6 +266,16 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi _processM83(line); break; } + case 106: // Set fan speed + { + _processM106(line); + break; + } + case 107: // Disable fan + { + _processM107(line); + break; + } case 108: case 135: { @@ -448,6 +465,24 @@ void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line) _set_e_local_positioning_type(Relative); } +void GCodeAnalyzer::_processM106(const GCodeReader::GCodeLine& line) +{ + if (!line.has('P')) + { + // The absence of P means the print cooling fan, so ignore anything else. + float new_fan_speed; + if (line.has_value('S', new_fan_speed)) + _set_fan_speed((100.0f / 256.0f) * new_fan_speed); + else + _set_fan_speed(100.0f); + } +} + +void GCodeAnalyzer::_processM107(const GCodeReader::GCodeLine& line) +{ + _set_fan_speed(0.0f); +} + void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line) { // These M-codes are used by MakerWare and Sailfish to change active tool. @@ -726,6 +761,16 @@ float GCodeAnalyzer::_get_feedrate() const return m_state.data.feedrate; } +void GCodeAnalyzer::_set_fan_speed(float fan_speed_percentage) +{ + m_state.data.fan_speed = fan_speed_percentage; +} + +float GCodeAnalyzer::_get_fan_speed() const +{ + return m_state.data.fan_speed; +} + void GCodeAnalyzer::_set_axis_position(EAxis axis, float position) { m_state.position[axis] = position; @@ -798,7 +843,7 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) Vec3d start_position = _get_start_position() + extruder_offset; Vec3d end_position = _get_end_position() + extruder_offset; - it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id()); + it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); } bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const @@ -834,6 +879,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ path.polyline = polyline; path.feedrate = data.feedrate; path.extruder_id = data.extruder_id; + path.fan_speed = data.fan_speed; path.cp_color_id = data.cp_color_id; get_layer_at_z(preview_data.extrusion.layers, z).paths.push_back(path); @@ -854,6 +900,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ GCodePreviewData::Range width_range; GCodePreviewData::Range feedrate_range; GCodePreviewData::Range volumetric_rate_range; + GCodePreviewData::Range fan_speed_range; // to avoid to call the callback too often unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1); @@ -888,6 +935,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ width_range.update_from(move.data.width); feedrate_range.update_from(move.data.feedrate); volumetric_rate_range.update_from(volumetric_rate); + fan_speed_range.update_from(move.data.fan_speed); } else // append end vertex of the move to current polyline @@ -906,6 +954,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ preview_data.ranges.width.update_from(width_range); preview_data.ranges.feedrate.update_from(feedrate_range); preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range); + preview_data.ranges.fan_speed.update_from(fan_speed_range); // we need to sort the layers by their z as they can be shuffled in case of sequential prints std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; }); diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index 0372d9da72..529610b0b2 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -54,10 +54,11 @@ public: float width; // mm float height; // mm float feedrate; // mm/s + float fan_speed; // percentage unsigned int cp_color_id; Metadata(); - Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id = 0); + Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id = 0); bool operator != (const Metadata& other) const; }; @@ -81,7 +82,7 @@ public: Vec3d end_position; float delta_extruder; - GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id = 0); + GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id = 0); GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder); }; @@ -171,6 +172,12 @@ private: // Set extruder to relative mode void _processM83(const GCodeReader::GCodeLine& line); + // Set fan speed + void _processM106(const GCodeReader::GCodeLine& line); + + // Disable fan + void _processM107(const GCodeReader::GCodeLine& line); + // Set tool (MakerWare and Sailfish flavor) void _processM108orM135(const GCodeReader::GCodeLine& line); @@ -233,6 +240,9 @@ private: void _set_feedrate(float feedrate_mm_sec); float _get_feedrate() const; + void _set_fan_speed(float fan_speed_percentage); + float _get_fan_speed() const; + void _set_axis_position(EAxis axis, float position); float _get_axis_position(EAxis axis) const; diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 76f21daeb5..c6cfcc8afc 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -241,6 +241,7 @@ void GCodePreviewData::set_default() ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); extrusion.set_default(); @@ -287,6 +288,11 @@ GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) con return ranges.feedrate.get_color_at(feedrate); } +GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const +{ + return ranges.fan_speed.get_color_at(fan_speed); +} + GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const { return ranges.volumetric_rate.get_color_at(rate); @@ -358,6 +364,8 @@ std::string GCodePreviewData::get_legend_title() const return L("Width (mm)"); case Extrusion::Feedrate: return L("Speed (mm/s)"); + case Extrusion::FanSpeed: + return L("Fan Speed (%)"); case Extrusion::VolumetricRate: return L("Volumetric flow rate (mm³/s)"); case Extrusion::Tool: @@ -421,6 +429,11 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f); break; } + case Extrusion::FanSpeed: + { + Helper::FillListFromRange(items, ranges.fan_speed, 0, 1.0f); + break; + } case Extrusion::VolumetricRate: { Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f); diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 6490399b42..7f5b691e14 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -52,6 +52,8 @@ public: Range width; // Color mapping by feedrate. Range feedrate; + // Color mapping by fan speed. + Range fan_speed; // Color mapping by volumetric extrusion rate. Range volumetric_rate; }; @@ -74,6 +76,7 @@ public: Height, Width, Feedrate, + FanSpeed, VolumetricRate, Tool, ColorPrint, @@ -205,6 +208,7 @@ public: Color get_height_color(float height) const; Color get_width_color(float width) const; Color get_feedrate_color(float feedrate) const; + Color get_fan_speed_color(float fan_speed) const; Color get_volumetric_rate_color(float rate) const; void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 715d9f806a..7656691ce6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5001,6 +5001,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat return path.width; case GCodePreviewData::Extrusion::Feedrate: return path.feedrate; + case GCodePreviewData::Extrusion::FanSpeed: + return path.fan_speed; case GCodePreviewData::Extrusion::VolumetricRate: return path.feedrate * (float)path.mm3_per_mm; case GCodePreviewData::Extrusion::Tool: @@ -5026,6 +5028,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat return data.get_width_color(value); case GCodePreviewData::Extrusion::Feedrate: return data.get_feedrate_color(value); + case GCodePreviewData::Extrusion::FanSpeed: + return data.get_fan_speed_color(value); case GCodePreviewData::Extrusion::VolumetricRate: return data.get_volumetric_rate_color(value); case GCodePreviewData::Extrusion::Tool: diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 7c761ed5fe..b6350bcabc 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -221,6 +221,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_choice_view_type->Append(_(L("Height"))); m_choice_view_type->Append(_(L("Width"))); m_choice_view_type->Append(_(L("Speed"))); + m_choice_view_type->Append(_(L("Fan speed"))); m_choice_view_type->Append(_(L("Volumetric flow rate"))); m_choice_view_type->Append(_(L("Tool"))); m_choice_view_type->Append(_(L("Color Print"))); From 661c91a8213a88d0783eb784ead47ed168c5dcd5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 27 Sep 2019 11:31:16 +0200 Subject: [PATCH 02/10] Fixing Linux build GCC 4.8 does not fully support C++11 and in-class char array initialization --- src/admesh/stl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 43999d365b..fa0edec2ba 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -90,7 +90,7 @@ struct stl_neighbors { struct stl_stats { stl_stats() { memset(&header, 0, 81); } - char header[81] = ""; + char header[81]; stl_type type = (stl_type)0; uint32_t number_of_facets = 0; stl_vertex max = stl_vertex::Zero(); From 5123673b3a7ec590e74798b331400a849542b7d8 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 27 Sep 2019 11:42:52 +0200 Subject: [PATCH 03/10] CMakeLists: filename fix so everything works on case-sensitive filesystems --- src/libslic3r/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 6fe68e56fd..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 - KdTreeIndirect.hpp + KDTreeIndirect.hpp Layer.cpp Layer.hpp LayerRegion.cpp From d57a09558e692dc1f0975c8487dc33603762d5d9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 27 Sep 2019 13:26:58 +0200 Subject: [PATCH 04/10] Several fixes of previous commits related to KDTreeIndirect.hpp and ShortestPath.cpp/.hpp --- src/libslic3r/KDTreeIndirect.hpp | 7 +++++-- src/libslic3r/ShortestPath.cpp | 13 +++++++------ src/libslic3r/ShortestPath.hpp | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp index f79dab9b36..4f71dd3e4d 100644 --- a/src/libslic3r/KDTreeIndirect.hpp +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -19,7 +19,10 @@ public: static constexpr size_t NumDimensions = ANumDimensions; using CoordinateFn = ACoordinateFn; using CoordType = ACoordType; - static constexpr size_t npos = size_t(-1); + // Following could be static constexpr size_t, but that would not link in C++11 + enum : size_t { + npos = size_t(-1) + }; KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {} KDTreeIndirect(CoordinateFn coordinate, std::vector indices) : coordinate(coordinate) { this->build(std::move(indices)); } @@ -69,7 +72,7 @@ public: template void visit(Visitor &visitor) const { - return m_nodes.empty() ? npos : visit_recursive(0, 0, visitor); + visit_recursive(0, 0, visitor); } CoordinateFn coordinate; diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index b31a9d1f38..490bcf1dcc 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -4,6 +4,7 @@ #undef assert #endif +#include "clipper.hpp" #include "ShortestPath.hpp" #include "KDTreeIndirect.hpp" #include "MutablePriorityQueue.hpp" @@ -34,7 +35,7 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin { // Just sort the end points so that the first point visited is closest to start_near. out.emplace_back(0, start_near != nullptr && - (end_point_func(0, true) - *start_near).cast().squaredNorm() < (end_point_func(0, false) - *start_near).cast().squaredNorm()); + (end_point_func(0, true) - *start_near).template cast().squaredNorm() < (end_point_func(0, false) - *start_near).template cast().squaredNorm()); } else { @@ -55,8 +56,8 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin std::vector end_points; end_points.reserve(num_segments * 2); for (size_t i = 0; i < num_segments; ++ i) { - end_points.emplace_back(end_point_func(i, true ).cast()); - end_points.emplace_back(end_point_func(i, false).cast()); + end_points.emplace_back(end_point_func(i, true ).template cast()); + end_points.emplace_back(end_point_func(i, false).template cast()); } // Construct the closest point KD tree over end points of segments. @@ -125,7 +126,7 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin EndPoint *first_point = nullptr; size_t first_point_idx = std::numeric_limits::max(); if (start_near != nullptr) { - size_t idx = find_closest_point(kdtree, start_near->cast()); + size_t idx = find_closest_point(kdtree, start_near->template cast()); assert(idx < end_points.size()); first_point = &end_points[idx]; first_point->distance_out = 0.; @@ -309,7 +310,7 @@ std::vector> chain_extrusion_entities(std::vector &entities, std::vector> &chain) +void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain) { assert(entities.size() == chain.size()); std::vector out; @@ -339,7 +340,7 @@ std::vector chain_points(const Points &points, Point *start_near) return out; } -Polylines chain_infill_polylines(Polylines &polylines) +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); diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index b4d0b4737b..06081e1fc0 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -15,10 +15,10 @@ namespace Slic3r { std::vector chain_points(const Points &points, Point *start_near = nullptr); std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); -void reorder_extrusion_entities(std::vector &entities, std::vector> &chain); +void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain); void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); -Polylines chain_infill_polylines(Polylines &src); +Polylines chain_infill_polylines(Polylines &&src); std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); From 331c187b39e9a800eb826267eae09994ca17278d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Sep 2019 18:17:21 +0200 Subject: [PATCH 05/10] Rest of the path chaining has been replaced with the new algorithm. PolylineCollection.cpp/hpp was removed, use Polylines instead. Various first_point() / last_point() now return references, not copies. --- src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/ExtrusionEntity.hpp | 16 +-- src/libslic3r/ExtrusionEntityCollection.cpp | 96 ++++--------- src/libslic3r/ExtrusionEntityCollection.hpp | 9 +- src/libslic3r/Fill/Fill3DHoneycomb.cpp | 2 +- src/libslic3r/Fill/FillGyroid.cpp | 2 +- src/libslic3r/Fill/FillHoneycomb.cpp | 2 +- src/libslic3r/Fill/FillPlanePath.cpp | 1 - src/libslic3r/Fill/FillRectilinear.cpp | 2 +- src/libslic3r/GCode.cpp | 6 +- src/libslic3r/Geometry.cpp | 1 - src/libslic3r/KDTreeIndirect.hpp | 4 +- src/libslic3r/Layer.hpp | 4 +- src/libslic3r/LayerRegion.cpp | 2 +- src/libslic3r/MultiPoint.hpp | 2 +- src/libslic3r/PerimeterGenerator.cpp | 7 +- src/libslic3r/Polygon.cpp | 88 ++---------- src/libslic3r/Polygon.hpp | 25 ++-- src/libslic3r/Polyline.cpp | 29 ++-- src/libslic3r/Polyline.hpp | 15 +- src/libslic3r/PolylineCollection.cpp | 92 ------------ src/libslic3r/PolylineCollection.hpp | 47 ------- src/libslic3r/ShortestPath.cpp | 146 ++++++++++++++++++-- src/libslic3r/ShortestPath.hpp | 6 +- src/libslic3r/SupportMaterial.cpp | 2 +- xs/xsp/ExtrusionEntityCollection.xsp | 8 +- xs/xsp/Filler.xsp | 1 - xs/xsp/Layer.xsp | 2 - xs/xsp/PolylineCollection.xsp | 11 +- 29 files changed, 266 insertions(+), 364 deletions(-) delete mode 100644 src/libslic3r/PolylineCollection.cpp delete mode 100644 src/libslic3r/PolylineCollection.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 6fe68e56fd..78302e291e 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -131,8 +131,6 @@ add_library(libslic3r STATIC PolygonTrimmer.hpp Polyline.cpp Polyline.hpp - PolylineCollection.cpp - PolylineCollection.hpp Print.cpp Print.hpp PrintBase.cpp diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index dfc180689b..8ec495b6ca 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -81,8 +81,8 @@ public: virtual ExtrusionEntity* clone_move() = 0; virtual ~ExtrusionEntity() {} virtual void reverse() = 0; - virtual Point first_point() const = 0; - virtual Point last_point() const = 0; + virtual const Point& first_point() const = 0; + virtual const Point& last_point() const = 0; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0; @@ -139,8 +139,8 @@ public: // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); } void reverse() override { this->polyline.reverse(); } - Point first_point() const override { return this->polyline.points.front(); } - Point last_point() const override { return this->polyline.points.back(); } + const Point& first_point() const override { return this->polyline.points.front(); } + const Point& last_point() const override { return this->polyline.points.back(); } size_t size() const { return this->polyline.size(); } bool empty() const { return this->polyline.empty(); } bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } @@ -200,8 +200,8 @@ public: // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); } void reverse() override; - Point first_point() const override { return this->paths.front().polyline.points.front(); } - Point last_point() const override { return this->paths.back().polyline.points.back(); } + const Point& first_point() const override { return this->paths.front().polyline.points.front(); } + const Point& last_point() const override { return this->paths.back().polyline.points.back(); } double length() const override; ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. @@ -243,8 +243,8 @@ public: bool make_clockwise(); bool make_counter_clockwise(); void reverse() override; - Point first_point() const override { return this->paths.front().polyline.points.front(); } - Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } + const Point& first_point() const override { return this->paths.front().polyline.points.front(); } + const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } Polygon polygon() const; double length() const override; bool split_at_vertex(const Point &point); diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index f7fab1ba1b..8c7e00e60b 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -1,4 +1,5 @@ #include "ExtrusionEntityCollection.hpp" +#include "ShortestPath.hpp" #include #include #include @@ -73,78 +74,31 @@ void ExtrusionEntityCollection::remove(size_t i) this->entities.erase(this->entities.begin() + i); } -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const +ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const { - ExtrusionEntityCollection coll; - this->chained_path(&coll, no_reverse, role); - return coll; -} - -void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const -{ - if (this->entities.empty()) return; - this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role); -} - -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const -{ - ExtrusionEntityCollection coll; - this->chained_path_from(start_near, &coll, no_reverse, role); - return coll; -} - -void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const -{ - if (this->no_sort) { - *retval = *this; - return; - } - - retval->entities.reserve(this->entities.size()); - - // if we're asked to return the original indices, build a map - std::map indices_map; - - ExtrusionEntitiesPtr my_paths; - for (ExtrusionEntity * const &entity_src : this->entities) { - if (role != erMixed) { - // The caller wants only paths with a specific extrusion role. - auto role2 = entity_src->role(); - if (role != role2) { - // This extrusion entity does not match the role asked. - assert(role2 != erMixed); - continue; - } - } - - ExtrusionEntity *entity = entity_src->clone(); - my_paths.push_back(entity); -// if (orig_indices != nullptr) -// indices_map[entity] = &entity_src - &this->entities.front(); - } - - Points endpoints; - for (const ExtrusionEntity *entity : my_paths) { - endpoints.push_back(entity->first_point()); - endpoints.push_back((no_reverse || ! entity->can_reverse()) ? - entity->first_point() : entity->last_point()); - } - - while (! my_paths.empty()) { - // find nearest point - int start_index = start_near.nearest_point_index(endpoints); - int path_index = start_index/2; - ExtrusionEntity* entity = my_paths.at(path_index); - // never reverse loops, since it's pointless for chained path and callers might depend on orientation - if (start_index % 2 && !no_reverse && entity->can_reverse()) - entity->reverse(); - retval->entities.push_back(my_paths.at(path_index)); -// if (orig_indices != nullptr) -// orig_indices->push_back(indices_map[entity]); - my_paths.erase(my_paths.begin() + path_index); - endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); - start_near = retval->entities.back()->last_point(); - } + ExtrusionEntityCollection out; + if (this->no_sort) { + out = *this; + } else { + if (role == erMixed) + out = *this; + else { + for (const ExtrusionEntity *ee : this->entities) { + if (role != erMixed) { + // The caller wants only paths with a specific extrusion role. + auto role2 = ee->role(); + if (role != role2) { + // This extrusion entity does not match the role asked. + assert(role2 != erMixed); + continue; + } + } + out.entities.emplace_back(ee->clone()); + } + } + chain_and_reorder_extrusion_entities(out.entities, &start_near); + } + return out; } void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index e92fa156f5..4e14917240 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -65,13 +65,10 @@ public: } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const; - void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const; - ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const; - void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const; + ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const; void reverse(); - Point first_point() const { return this->entities.front()->first_point(); } - Point last_point() const { return this->entities.back()->last_point(); } + const Point& first_point() const { return this->entities.front()->first_point(); } + const Point& last_point() const { return this->entities.back()->last_point(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 44267d3a47..820f0008bf 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -176,7 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single( } } bool first = true; - for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { + for (Polyline &polyline : chain_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 09f0782bc9..7cd9558923 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -167,7 +167,7 @@ void FillGyroid::_fill_surface_single( } } bool first = true; - for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { + for (Polyline &polyline : chain_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; diff --git a/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp index be569c2d84..948af182bb 100644 --- a/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/src/libslic3r/Fill/FillHoneycomb.cpp @@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single( // connect paths if (! paths.empty()) { // prevent calling leftmost_point() on empty collections - Polylines chained = chain_infill_polylines(std::move(paths)); + Polylines chained = chain_polylines(std::move(paths)); assert(paths.empty()); paths.clear(); for (Polyline &path : chained) { diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index 3b9266a0fe..7a322ce991 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -1,5 +1,4 @@ #include "../ClipperUtils.hpp" -#include "../PolylineCollection.hpp" #include "../Surface.hpp" #include "FillPlanePath.hpp" diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index d535ba29b5..629e5b6f46 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -93,7 +93,7 @@ void FillRectilinear::_fill_surface_single( } } bool first = true; - for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { + for (Polyline &polyline : chain_polylines(std::move(polylines))) { if (! first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 348834b3c4..efcf15ad58 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1969,7 +1969,7 @@ void GCode::process_layer( m_layer = layers[instance_to_print.layer_id].support_layer; gcode += this->extrude_support( // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. - instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, false, instance_to_print.object_by_extruder.support_extrusion_role)); + instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role)); m_layer = layers[instance_to_print.layer_id].layer(); } for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { @@ -2588,10 +2588,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig()); - for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) { + for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos).entities) { auto *eec = dynamic_cast(fill); if (eec) { - for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities) + for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) gcode += this->extrude_entity(*ee, "infill"); } else gcode += this->extrude_entity(*fill, "infill"); diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 8cf4047a1a..e80b365bb5 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -3,7 +3,6 @@ #include "ClipperUtils.hpp" #include "ExPolygon.hpp" #include "Line.hpp" -#include "PolylineCollection.hpp" #include "clipper.hpp" #include #include diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp index f79dab9b36..e7526210d4 100644 --- a/src/libslic3r/KDTreeIndirect.hpp +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -61,8 +61,10 @@ public: { CoordType dist = point_coord - this->coordinate(idx, dimension); return (dist * dist < search_radius + CoordType(EPSILON)) ? + // The plane intersects a hypersphere centered at point_coord of search_radius. ((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) : - (dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); + // The plane does not intersect the hypersphere. + (dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); } // Visitor is supposed to return a bit mask of VisitorReturnMask. diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 555017207a..539ae39253 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -6,8 +6,6 @@ #include "SurfaceCollection.hpp" #include "ExtrusionEntityCollection.hpp" #include "ExPolygonCollection.hpp" -#include "PolylineCollection.hpp" - namespace Slic3r { @@ -48,7 +46,7 @@ public: Polygons bridged; // collection of polylines representing the unsupported bridge edges - PolylineCollection unsupported_bridge_edges; + Polylines unsupported_bridge_edges; // ordered collection of extrusion paths/loops to build all perimeters // (this collection contains only ExtrusionEntityCollection objects) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index bfe96d311c..d13549bf4c 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -272,7 +272,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly bridges[idx_last].bridge_angle = bd.angle; if (this->layer()->object()->config().support_material) { polygons_append(this->bridged, bd.coverage()); - this->unsupported_bridge_edges.append(bd.unsupported_edges()); + append(this->unsupported_bridge_edges, bd.unsupported_edges()); } } else if (custom_angle > 0) { // Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 38020d6e84..7cd6d7834a 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -34,7 +34,7 @@ public: void rotate(double angle, const Point ¢er); void reverse(); Point first_point() const; - virtual Point last_point() const = 0; + virtual const Point& last_point() const = 0; virtual Lines lines() const = 0; size_t size() const { return points.size(); } bool empty() const { return points.empty(); } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index cd53547769..450fff3515 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -175,10 +175,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime perimeter_generator.overhang_flow.width, perimeter_generator.overhang_flow.height); - // reapply the nearest point search for starting point - // We allow polyline reversal because Clipper may have randomly - // reversed polylines during clipping. - paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path(); + // Reapply the nearest point search for starting point. + // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. + chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); } else { ExtrusionPath path(role); path.polyline = loop.polygon.split_at_first_point(); diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index cf0783bae5..5ef5d0ceb1 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -5,43 +5,12 @@ namespace Slic3r { -Polygon::operator Polygons() const -{ - Polygons pp; - pp.push_back(*this); - return pp; -} - -Polygon::operator Polyline() const -{ - return this->split_at_first_point(); -} - -Point& -Polygon::operator[](Points::size_type idx) -{ - return this->points[idx]; -} - -const Point& -Polygon::operator[](Points::size_type idx) const -{ - return this->points[idx]; -} - -Point -Polygon::last_point() const -{ - return this->points.front(); // last point == first point for polygons -} - Lines Polygon::lines() const { return to_lines(*this); } -Polyline -Polygon::split_at_vertex(const Point &point) const +Polyline Polygon::split_at_vertex(const Point &point) const { // find index of point for (const Point &pt : this->points) @@ -52,8 +21,7 @@ Polygon::split_at_vertex(const Point &point) const } // Split a closed polygon into an open polyline, with the split point duplicated at both ends. -Polyline -Polygon::split_at_index(int index) const +Polyline Polygon::split_at_index(int index) const { Polyline polyline; polyline.points.reserve(this->points.size() + 1); @@ -64,19 +32,6 @@ Polygon::split_at_index(int index) const return polyline; } -// Split a closed polygon into an open polyline, with the split point duplicated at both ends. -Polyline -Polygon::split_at_first_point() const -{ - return this->split_at_index(0); -} - -Points -Polygon::equally_spaced_points(double distance) const -{ - return this->split_at_first_point().equally_spaced_points(distance); -} - /* int64_t Polygon::area2x() const { @@ -107,20 +62,17 @@ double Polygon::area() const return 0.5 * a; } -bool -Polygon::is_counter_clockwise() const +bool Polygon::is_counter_clockwise() const { return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); } -bool -Polygon::is_clockwise() const +bool Polygon::is_clockwise() const { return !this->is_counter_clockwise(); } -bool -Polygon::make_counter_clockwise() +bool Polygon::make_counter_clockwise() { if (!this->is_counter_clockwise()) { this->reverse(); @@ -129,8 +81,7 @@ Polygon::make_counter_clockwise() return false; } -bool -Polygon::make_clockwise() +bool Polygon::make_clockwise() { if (this->is_counter_clockwise()) { this->reverse(); @@ -139,16 +90,9 @@ Polygon::make_clockwise() return false; } -bool -Polygon::is_valid() const -{ - return this->points.size() >= 3; -} - // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. -bool -Polygon::contains(const Point &point) const +bool Polygon::contains(const Point &point) const { // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html bool result = false; @@ -174,8 +118,7 @@ Polygon::contains(const Point &point) const } // this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons() -Polygons -Polygon::simplify(double tolerance) const +Polygons Polygon::simplify(double tolerance) const { // repeat first point at the end in order to apply Douglas-Peucker // on the whole polygon @@ -189,8 +132,7 @@ Polygon::simplify(double tolerance) const return simplify_polygons(pp); } -void -Polygon::simplify(double tolerance, Polygons &polygons) const +void Polygon::simplify(double tolerance, Polygons &polygons) const { Polygons pp = this->simplify(tolerance); polygons.reserve(polygons.size() + pp.size()); @@ -198,8 +140,7 @@ Polygon::simplify(double tolerance, Polygons &polygons) const } // Only call this on convex polygons or it will return invalid results -void -Polygon::triangulate_convex(Polygons* polygons) const +void Polygon::triangulate_convex(Polygons* polygons) const { for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) { Polygon p; @@ -214,8 +155,7 @@ Polygon::triangulate_convex(Polygons* polygons) const } // center of mass -Point -Polygon::centroid() const +Point Polygon::centroid() const { double area_temp = this->area(); double x_temp = 0; @@ -232,8 +172,7 @@ Polygon::centroid() const // find all concave vertices (i.e. having an internal angle greater than the supplied angle) // (external = right side, thus we consider ccw orientation) -Points -Polygon::concave_points(double angle) const +Points Polygon::concave_points(double angle) const { Points points; angle = 2*PI - angle; @@ -256,8 +195,7 @@ Polygon::concave_points(double angle) const // find all convex vertices (i.e. having an internal angle smaller than the supplied angle) // (external = right side, thus we consider ccw orientation) -Points -Polygon::convex_points(double angle) const +Points Polygon::convex_points(double angle) const { Points points; angle = 2*PI - angle; diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 63162d9539..42f938c726 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -13,13 +13,14 @@ namespace Slic3r { class Polygon; typedef std::vector Polygons; -class Polygon : public MultiPoint { +class Polygon : public MultiPoint +{ public: - operator Polygons() const; - operator Polyline() const; - Point& operator[](Points::size_type idx); - const Point& operator[](Points::size_type idx) const; - + operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; } + operator Polyline() const { return this->split_at_first_point(); } + Point& operator[](Points::size_type idx) { return this->points[idx]; } + const Point& operator[](Points::size_type idx) const { return this->points[idx]; } + Polygon() {} explicit Polygon(const Points &points): MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} @@ -34,20 +35,24 @@ public: Polygon& operator=(const Polygon &other) { points = other.points; return *this; } Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } - Point last_point() const; + // last point == first point for polygons + const Point& last_point() const override { return this->points.front(); } + virtual Lines lines() const; Polyline split_at_vertex(const Point &point) const; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_index(int index) const; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. - Polyline split_at_first_point() const; - Points equally_spaced_points(double distance) const; + Polyline split_at_first_point() const { return this->split_at_index(0); } + Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); } + double area() const; bool is_counter_clockwise() const; bool is_clockwise() const; bool make_counter_clockwise(); bool make_clockwise(); - bool is_valid() const; + bool is_valid() const { return this->points.size() >= 3; } + // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. bool contains(const Point &point) const; diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 531383ae14..26aad83d2b 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -23,18 +23,17 @@ Polyline::operator Line() const return Line(this->points.front(), this->points.back()); } -Point -Polyline::leftmost_point() const +const Point& Polyline::leftmost_point() const { - Point p = this->points.front(); - for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { - if ((*it)(0) < p(0)) p = *it; + const Point *p = &this->points.front(); + for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++ it) { + if (it->x() < p->x()) + p = &(*it); } - return p; + return *p; } -Lines -Polyline::lines() const +Lines Polyline::lines() const { Lines lines; if (this->points.size() >= 2) { @@ -205,6 +204,20 @@ BoundingBox get_extents(const Polylines &polylines) return bb; } +const Point& leftmost_point(const Polylines &polylines) +{ + if (polylines.empty()) + throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); + Polylines::const_iterator it = polylines.begin(); + const Point *p = &it->leftmost_point(); + for (++ it; it != polylines.end(); ++it) { + const Point *p2 = &it->leftmost_point(); + if (p2->x() < p->x()) + p = p2; + } + return *p; +} + bool remove_degenerate(Polylines &polylines) { bool modified = false; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index d595f252d6..7e3a1e5065 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -62,9 +62,9 @@ public: operator Polylines() const; operator Line() const; - Point last_point() const override { return this->points.back(); } + const Point& last_point() const override { return this->points.back(); } - Point leftmost_point() const; + const Point& leftmost_point() const; virtual Lines lines() const; void clip_end(double distance); void clip_start(double distance); @@ -77,6 +77,15 @@ public: bool is_straight() const; }; +// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests! +#ifdef PERL_UCHAR_MIN +class PolylineCollection +{ +public: + Polylines polylines; +}; +#endif /* PERL_UCHAR_MIN */ + extern BoundingBox get_extents(const Polyline &polyline); extern BoundingBox get_extents(const Polylines &polylines); @@ -129,6 +138,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src) } } +const Point& leftmost_point(const Polylines &polylines); + bool remove_degenerate(Polylines &polylines); class ThickPolyline : public Polyline { diff --git a/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp deleted file mode 100644 index 0c66c371a9..0000000000 --- a/src/libslic3r/PolylineCollection.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "PolylineCollection.hpp" - -namespace Slic3r { - -struct Chaining -{ - Point first; - Point last; - size_t idx; -}; - -template -inline int nearest_point_index(const std::vector &pairs, const Point &start_near, bool no_reverse) -{ - T dmin = std::numeric_limits::max(); - int idx = 0; - for (std::vector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { - T d = sqr(T(start_near(0) - it->first(0))); - if (d <= dmin) { - d += sqr(T(start_near(1) - it->first(1))); - if (d < dmin) { - idx = (it - pairs.begin()) * 2; - dmin = d; - if (dmin < EPSILON) - break; - } - } - if (! no_reverse) { - d = sqr(T(start_near(0) - it->last(0))); - if (d <= dmin) { - d += sqr(T(start_near(1) - it->last(1))); - if (d < dmin) { - idx = (it - pairs.begin()) * 2 + 1; - dmin = d; - if (dmin < EPSILON) - break; - } - } - } - } - return idx; -} - -Polylines PolylineCollection::_chained_path_from( - const Polylines &src, - Point start_near, - bool no_reverse, - bool move_from_src) -{ - std::vector endpoints; - endpoints.reserve(src.size()); - for (size_t i = 0; i < src.size(); ++ i) { - Chaining c; - c.first = src[i].first_point(); - if (! no_reverse) - c.last = src[i].last_point(); - c.idx = i; - endpoints.push_back(c); - } - Polylines retval; - while (! endpoints.empty()) { - // find nearest point - int endpoint_index = nearest_point_index(endpoints, start_near, no_reverse); - assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2); - if (move_from_src) { - retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); - } else { - retval.push_back(src[endpoints[endpoint_index/2].idx]); - } - if (endpoint_index & 1) - retval.back().reverse(); - endpoints.erase(endpoints.begin() + endpoint_index/2); - start_near = retval.back().last_point(); - } - return retval; -} - -Point PolylineCollection::leftmost_point(const Polylines &polylines) -{ - if (polylines.empty()) - throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); - Polylines::const_iterator it = polylines.begin(); - Point p = it->leftmost_point(); - for (++ it; it != polylines.end(); ++it) { - Point p2 = it->leftmost_point(); - if (p2(0) < p(0)) - p = p2; - } - return p; -} - -} // namespace Slic3r diff --git a/src/libslic3r/PolylineCollection.hpp b/src/libslic3r/PolylineCollection.hpp deleted file mode 100644 index 87fc1985b8..0000000000 --- a/src/libslic3r/PolylineCollection.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef slic3r_PolylineCollection_hpp_ -#define slic3r_PolylineCollection_hpp_ - -#include "libslic3r.h" -#include "Polyline.hpp" - -namespace Slic3r { - -class PolylineCollection -{ - static Polylines _chained_path_from( - const Polylines &src, - Point start_near, - bool no_reverse, - bool move_from_src); - -public: - Polylines polylines; - void chained_path(PolylineCollection* retval, bool no_reverse = false) const - { retval->polylines = chained_path(this->polylines, no_reverse); } - void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const - { retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); } - Point leftmost_point() const - { return leftmost_point(polylines); } - void append(const Polylines &polylines) - { this->polylines.insert(this->polylines.end(), polylines.begin(), polylines.end()); } - - static Point leftmost_point(const Polylines &polylines); - static Polylines chained_path(Polylines &&src, bool no_reverse = false) { - return (src.empty() || src.front().points.empty()) ? - Polylines() : - _chained_path_from(src, src.front().first_point(), no_reverse, true); - } - static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false) - { return _chained_path_from(src, start_near, no_reverse, true); } - static Polylines chained_path(const Polylines &src, bool no_reverse = false) { - return (src.empty() || src.front().points.empty()) ? - Polylines() : - _chained_path_from(src, src.front().first_point(), no_reverse, false); - } - static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false) - { return _chained_path_from(src, start_near, no_reverse, false); } -}; - -} - -#endif diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index b31a9d1f38..0fc16e74a1 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -14,6 +14,44 @@ namespace Slic3r { +// Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor. +// This implementation will always produce valid result even if some segments cannot reverse. +template +std::vector> chain_segments_closest_point(std::vector &end_points, KDTreeType &kdtree, CouldReverseFunc &could_reverse_func, EndPointType &first_point) +{ + assert((end_points.size() & 1) == 0); + size_t num_segments = end_points.size() / 2; + assert(num_segments >= 2); + for (EndPointType &ep : end_points) + ep.chain_id = 0; + std::vector> out; + out.reserve(num_segments); + size_t first_point_idx = &first_point - end_points.data(); + out.emplace_back(first_point_idx / 2, (first_point_idx & 1) != 0); + first_point.chain_id = 1; + size_t this_idx = first_point_idx ^ 1; + for (int iter = (int)num_segments - 2; iter >= 0; -- iter) { + EndPointType &this_point = end_points[this_idx]; + this_point.chain_id = 1; + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda). + // Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it. + size_t next_idx = find_closest_point(kdtree, this_point.pos, + [this_idx, &end_points, &could_reverse_func](size_t idx) { + return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1)); + }); + assert(next_idx < end_points.size()); + EndPointType &end_point = end_points[next_idx]; + end_point.chain_id = 1; + this_idx = next_idx ^ 1; + } +#ifndef _NDEBUG + assert(end_points[this_idx].chain_id == 0); + for (EndPointType &ep : end_points) + assert(&ep == &end_points[this_idx] || ep.chain_id == 1); +#endif /* _NDEBUG */ + return out; +} + // Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm. // Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments. // Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle. @@ -22,8 +60,8 @@ namespace Slic3r { // The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which // is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates // a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities. -template -std::vector> chain_segments(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) +template +std::vector> chain_segments_greedy_constrained_reversals_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) { std::vector> out; @@ -132,6 +170,8 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin first_point->chain_id = equivalent_chain.next(); first_point_idx = idx; } + EndPoint *initial_point = first_point; + EndPoint *last_point = nullptr; // Assign the closest point and distance to the end points. for (EndPoint &end_point : end_points) { @@ -240,12 +280,15 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin if (iter == 0) { // Last iteration. There shall be exactly one or two end points waiting to be connected. assert(queue.size() == ((first_point == nullptr) ? 2 : 1)); - if (first_point == nullptr) + if (first_point == nullptr) { first_point = queue.top(); - while (! queue.empty()) { - queue.top()->edge_out = nullptr; queue.pop(); + first_point->edge_out = nullptr; } + last_point = queue.top(); + last_point->edge_out = nullptr; + queue.pop(); + assert(queue.empty()); break; } } else { @@ -280,24 +323,77 @@ std::vector> chain_segments(SegmentEndPointFunc end_poin // Now interconnect pairs of segments into a chain. assert(first_point != nullptr); + out.reserve(num_segments); + bool failed = false; do { assert(out.size() < num_segments); size_t first_point_id = first_point - &end_points.front(); size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; EndPoint *second_point = &end_points[first_point_id ^ 1]; - out.emplace_back(segment_id, (first_point_id & 1) != 0); + if (REVERSE_COULD_FAIL) { + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + } else { + assert(! reverse || could_reverse_func(segment_id)); + } + out.emplace_back(segment_id, reverse); first_point = second_point->edge_out; } while (first_point != nullptr); + if (REVERSE_COULD_FAIL) { + if (failed) { + if (start_near == nullptr) { + // We may try the reverse order. + out.clear(); + first_point = last_point; + failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + } + } + if (failed) + // As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints. + out = chain_segments_closest_point(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front()); + } else { + assert(! failed); + } } assert(out.size() == num_segments); return out; } +template +std::vector> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + return chain_segments_greedy_constrained_reversals_(end_point_func, could_reverse_func, num_segments, start_near); +} + +template +std::vector> chain_segments_greedy(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) +{ + auto could_reverse_func = [](size_t /* idx */) -> bool { return true; }; + return chain_segments_greedy_constrained_reversals_(end_point_func, could_reverse_func, num_segments, start_near); +} + std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near) { auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; - std::vector> out = chain_segments(segment_end_point, entities.size(), start_near); + auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; + std::vector> out = chain_segments_greedy_constrained_reversals(segment_end_point, could_reverse, entities.size(), start_near); for (size_t i = 0; i < entities.size(); ++ i) { ExtrusionEntity *ee = entities[i]; if (ee->is_loop()) @@ -328,10 +424,34 @@ void chain_and_reorder_extrusion_entities(std::vector &entitie reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); } +std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) +{ + auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; + return chain_segments_greedy(segment_end_point, extrusion_paths.size(), start_near); +} + +void reorder_extrusion_paths(std::vector &extrusion_paths, std::vector> &chain) +{ + assert(extrusion_paths.size() == chain.size()); + std::vector out; + out.reserve(extrusion_paths.size()); + for (const std::pair &idx : chain) { + out.emplace_back(std::move(extrusion_paths[idx.first])); + if (idx.second) + out.back().reverse(); + } + extrusion_paths.swap(out); +} + +void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) +{ + reorder_extrusion_paths(extrusion_paths, chain_extrusion_paths(extrusion_paths, start_near)); +} + std::vector chain_points(const Points &points, Point *start_near) { auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; }; - std::vector> ordered = chain_segments(segment_end_point, points.size(), start_near); + std::vector> ordered = chain_segments_greedy(segment_end_point, points.size(), start_near); std::vector out; out.reserve(ordered.size()); for (auto &segment_and_reversal : ordered) @@ -339,12 +459,12 @@ std::vector chain_points(const Points &points, Point *start_near) return out; } -Polylines chain_infill_polylines(Polylines &polylines) +Polylines chain_polylines(Polylines &polylines, const Point *start_near) { auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); }; - std::vector> ordered = chain_segments(segment_end_point, polylines.size(), nullptr); + std::vector> ordered = chain_segments_greedy(segment_end_point, polylines.size(), start_near); Polylines out; - out.reserve(polylines.size()); + out.reserve(polylines.size()); for (auto &segment_and_reversal : ordered) { out.emplace_back(std::move(polylines[segment_and_reversal.first])); if (segment_and_reversal.second) @@ -356,7 +476,7 @@ Polylines chain_infill_polylines(Polylines &polylines) template static inline T chain_path_items(const Points &points, const T &items) { auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; }; - std::vector> ordered = chain_segments(segment_end_point, points.size(), nullptr); + std::vector> ordered = chain_segments_greedy(segment_end_point, points.size(), nullptr); T out; out.reserve(items.size()); for (auto &segment_and_reversal : ordered) @@ -382,7 +502,7 @@ std::vector> chain_print_object_instances(const Print } } auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; }; - std::vector> ordered = chain_segments(segment_end_point, instances.size(), nullptr); + std::vector> ordered = chain_segments_greedy(segment_end_point, instances.size(), nullptr); std::vector> out; out.reserve(instances.size()); for (auto &segment_and_reversal : ordered) diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index b4d0b4737b..81e2c20e13 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -18,7 +18,11 @@ std::vector> chain_extrusion_entities(std::vector &entities, std::vector> &chain); void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); -Polylines chain_infill_polylines(Polylines &src); +std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); +void reorder_extrusion_paths(std::vector &extrusion_paths, std::vector> &chain); +void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); + +Polylines chain_polylines(Polylines &src, const Point *start_near = nullptr); std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index c0c8e9ea0a..a6648f108c 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -923,7 +923,7 @@ namespace SupportMaterialInternal { //FIXME add supports at regular intervals to support long bridges! bridges = diff(bridges, // Offset unsupported edges into polygons. - offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); // Remove bridged areas from the supported areas. contact_polygons = diff(contact_polygons, bridges, true); } diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index 1f16ae3d4b..a01788c56c 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -14,13 +14,17 @@ void clear(); ExtrusionEntityCollection* chained_path(bool no_reverse, ExtrusionRole role = erMixed) %code{% + if (no_reverse) + croak("no_reverse must be false"); RETVAL = new ExtrusionEntityCollection(); - THIS->chained_path(RETVAL, no_reverse, role); + *RETVAL = THIS->chained_path_from(THIS->entities.front()->first_point()); %}; ExtrusionEntityCollection* chained_path_from(Point* start_near, bool no_reverse, ExtrusionRole role = erMixed) %code{% + if (no_reverse) + croak("no_reverse must be false"); RETVAL = new ExtrusionEntityCollection(); - THIS->chained_path_from(*start_near, RETVAL, no_reverse, role); + *RETVAL = THIS->chained_path_from(*start_near, role); %}; Clone first_point(); Clone last_point(); diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index 34a6d33bee..647d851eb1 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -3,7 +3,6 @@ %{ #include #include "libslic3r/Fill/Fill.hpp" -#include "libslic3r/PolylineCollection.hpp" #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" %} diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index efd6c9ae63..fc94d51157 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -19,8 +19,6 @@ %code%{ RETVAL = &THIS->fill_surfaces; %}; Polygons bridged() %code%{ RETVAL = THIS->bridged; %}; - Ref unsupported_bridge_edges() - %code%{ RETVAL = &THIS->unsupported_bridge_edges; %}; Ref perimeters() %code%{ RETVAL = &THIS->perimeters; %}; Ref fills() diff --git a/xs/xsp/PolylineCollection.xsp b/xs/xsp/PolylineCollection.xsp index 7237be5bb1..d8bb41b497 100644 --- a/xs/xsp/PolylineCollection.xsp +++ b/xs/xsp/PolylineCollection.xsp @@ -2,7 +2,11 @@ %{ #include -#include "libslic3r/PolylineCollection.hpp" + +#include "libslic3r.h" +#include "Polyline.hpp" +#include "ShortestPath.hpp" + %} %name{Slic3r::Polyline::Collection} class PolylineCollection { @@ -14,16 +18,15 @@ PolylineCollection* chained_path(bool no_reverse) %code{% RETVAL = new PolylineCollection(); - THIS->chained_path(RETVAL, no_reverse); + RETVAL->polylines = chain_polylines(THIS->polylines, &THIS->polylines.front().first_point()); %}; PolylineCollection* chained_path_from(Point* start_near, bool no_reverse) %code{% RETVAL = new PolylineCollection(); - THIS->chained_path_from(*start_near, RETVAL, no_reverse); + RETVAL->polylines = chain_polylines(THIS->polylines, start_near); %}; int count() %code{% RETVAL = THIS->polylines.size(); %}; - Clone leftmost_point(); %{ PolylineCollection* From 50d3894e3298ebce4c826c888d35e964bafc7867 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Sep 2019 18:47:08 +0200 Subject: [PATCH 06/10] Fix of the ShortestPath.hpp interface: Provide non-move variant. --- src/libslic3r/ShortestPath.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index 1b91b59cb5..b06d165d3c 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -23,6 +23,7 @@ void reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr); +inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp)); } std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); From debd9e2f81afebe665fe391aa99107d58550be36 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Sep 2019 19:07:07 +0200 Subject: [PATCH 07/10] One more try, fixing a missing parameter. --- src/libslic3r/ShortestPath.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index b06d165d3c..cd342015d7 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -23,7 +23,7 @@ void reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr); -inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp)); } +inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); } std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); From 6d11bfe96a96058f6d81f16f023d275f264883ba Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Sep 2019 19:18:35 +0200 Subject: [PATCH 08/10] Fixing C++ issues, that the Visual Studio compiler did not report. --- src/libslic3r/ShortestPath.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 3e6cf166a5..a0fb050057 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -45,11 +45,11 @@ std::vector> chain_segments_closest_point(std::vector> chain_extrusion_paths(std::vector(segment_end_point, extrusion_paths.size(), start_near); } -void reorder_extrusion_paths(std::vector &extrusion_paths, std::vector> &chain) +void reorder_extrusion_paths(std::vector &extrusion_paths, const std::vector> &chain) { assert(extrusion_paths.size() == chain.size()); std::vector out; From 0abde9a2a845aa42d79cee500b6bc4c6a93b82c7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 27 Sep 2019 19:47:30 +0200 Subject: [PATCH 09/10] Return MultiPoint::first_point() by reference. --- src/libslic3r/MultiPoint.cpp | 36 +++++++----------------------------- src/libslic3r/MultiPoint.hpp | 8 +++++--- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index ee3b997476..39b07e7d86 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -3,11 +3,6 @@ namespace Slic3r { -MultiPoint::operator Points() const -{ - return this->points; -} - void MultiPoint::scale(double factor) { for (Point &pt : points) @@ -57,18 +52,7 @@ void MultiPoint::rotate(double angle, const Point ¢er) } } -void MultiPoint::reverse() -{ - std::reverse(this->points.begin(), this->points.end()); -} - -Point MultiPoint::first_point() const -{ - return this->points.front(); -} - -double -MultiPoint::length() const +double MultiPoint::length() const { Lines lines = this->lines(); double len = 0; @@ -78,8 +62,7 @@ MultiPoint::length() const return len; } -int -MultiPoint::find_point(const Point &point) const +int MultiPoint::find_point(const Point &point) const { for (const Point &pt : this->points) if (pt == point) @@ -87,21 +70,18 @@ MultiPoint::find_point(const Point &point) const return -1; // not found } -bool -MultiPoint::has_boundary_point(const Point &point) const +bool MultiPoint::has_boundary_point(const Point &point) const { double dist = (point.projection_onto(*this) - point).cast().norm(); return dist < SCALED_EPSILON; } -BoundingBox -MultiPoint::bounding_box() const +BoundingBox MultiPoint::bounding_box() const { return BoundingBox(this->points); } -bool -MultiPoint::has_duplicate_points() const +bool MultiPoint::has_duplicate_points() const { for (size_t i = 1; i < points.size(); ++i) if (points[i-1] == points[i]) @@ -109,8 +89,7 @@ MultiPoint::has_duplicate_points() const return false; } -bool -MultiPoint::remove_duplicate_points() +bool MultiPoint::remove_duplicate_points() { size_t j = 0; for (size_t i = 1; i < points.size(); ++i) { @@ -129,8 +108,7 @@ MultiPoint::remove_duplicate_points() return false; } -bool -MultiPoint::intersection(const Line& line, Point* intersection) const +bool MultiPoint::intersection(const Line& line, Point* intersection) const { Lines lines = this->lines(); for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 7cd6d7834a..9ff91b5022 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -17,7 +17,8 @@ class MultiPoint public: Points points; - operator Points() const; + operator Points() const { return this->points; } + MultiPoint() {} MultiPoint(const MultiPoint &other) : points(other.points) {} MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} @@ -32,8 +33,9 @@ public: void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } void rotate(double cos_angle, double sin_angle); void rotate(double angle, const Point ¢er); - void reverse(); - Point first_point() const; + void reverse() { std::reverse(this->points.begin(), this->points.end()); } + + const Point& first_point() const { return this->points.front(); } virtual const Point& last_point() const = 0; virtual Lines lines() const = 0; size_t size() const { return points.size(); } From 5ded376fe2a0ef897e9ee6b8d2f8f82dca17848c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Sat, 28 Sep 2019 09:57:26 +0200 Subject: [PATCH 10/10] An attempt to fix #2997 More fixes might be needed on the other platforms --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 19dedc7b0e..0835a0d40e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -131,7 +131,7 @@ ObjectList::ObjectList(wxWindow* parent) : { wxDataViewItemArray sels; GetSelections(sels); - if (sels.front() == m_last_selected_item) + if (! sels.empty() && sels.front() == m_last_selected_item) m_last_selected_item = sels.back(); else m_last_selected_item = event.GetItem();