From 9b60be0c1aeda35d81cb30fbef05e11c7e7351e3 Mon Sep 17 00:00:00 2001 From: supermerill Date: Fri, 29 May 2020 21:01:30 +0200 Subject: Initial working prototype for the milling post-process --- resources/ui_layout/milling.ui | 10 +- resources/ui_layout/print.ui | 7 +- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Config.hpp | 13 +- src/libslic3r/CustomGCode.cpp | 6 +- src/libslic3r/CustomGCode.hpp | 2 +- src/libslic3r/Extruder.cpp | 93 ++++++++- src/libslic3r/Extruder.hpp | 102 +++++++--- src/libslic3r/ExtrusionEntity.cpp | 1 + src/libslic3r/ExtrusionEntity.hpp | 5 +- src/libslic3r/GCode.cpp | 274 +++++++++++++++++++-------- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/GCode/CoolingBuffer.cpp | 10 +- src/libslic3r/GCode/PreviewData.cpp | 1 + src/libslic3r/GCode/ToolOrdering.cpp | 62 +++--- src/libslic3r/GCode/ToolOrdering.hpp | 40 ++-- src/libslic3r/GCode/WipeTower.cpp | 6 +- src/libslic3r/GCode/WipeTower.hpp | 14 +- src/libslic3r/GCodeWriter.cpp | 107 +++++++---- src/libslic3r/GCodeWriter.hpp | 57 ++++-- src/libslic3r/Layer.cpp | 66 +++++++ src/libslic3r/Layer.hpp | 7 +- src/libslic3r/LayerRegion.cpp | 15 +- src/libslic3r/Milling/MillingPostProcess.cpp | 169 +++++++++++++++++ src/libslic3r/Milling/MillingPostProcess.hpp | 49 +++++ src/libslic3r/PerimeterGenerator.cpp | 67 +++++-- src/libslic3r/PerimeterGenerator.hpp | 11 +- src/libslic3r/Print.cpp | 39 ++-- src/libslic3r/Print.hpp | 16 +- src/libslic3r/PrintConfig.cpp | 147 +++++++++++--- src/libslic3r/PrintConfig.hpp | 19 ++ src/libslic3r/PrintObject.cpp | 29 ++- src/libslic3r/PrintRegion.cpp | 6 +- src/libslic3r/Slicing.cpp | 2 +- src/libslic3r/Slicing.hpp | 2 +- src/slic3r/GUI/ConfigManipulation.cpp | 5 + src/slic3r/GUI/DoubleSlider.cpp | 4 +- src/slic3r/GUI/GUI_Preview.cpp | 2 + src/slic3r/GUI/Preset.cpp | 6 +- 39 files changed, 1135 insertions(+), 340 deletions(-) create mode 100644 src/libslic3r/Milling/MillingPostProcess.cpp create mode 100644 src/libslic3r/Milling/MillingPostProcess.hpp diff --git a/resources/ui_layout/milling.ui b/resources/ui_layout/milling.ui index 7ef56a379..59ac2e9de 100644 --- a/resources/ui_layout/milling.ui +++ b/resources/ui_layout/milling.ui @@ -1,4 +1,12 @@ page:idx:Milling:funnel group:Size - setting:idx:milling_diameter \ No newline at end of file + setting:idx:milling_diameter +#group:Position +# setting:idx:milling_offset +# setting:idx:milling_z_offset +group:Behavior + setting:idx:milling_z_lift +group:G-Code + setting:idx:full_width:height$15:milling_toolchange_start_gcode + setting:idx:full_width:height$15:milling_toolchange_end_gcode \ No newline at end of file diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index d4fa0b803..df034abec 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -278,7 +278,12 @@ group:Output file setting:gcode_comments setting:gcode_label_objects setting:full_width:output_filename_format -group:Post-processing scripts +group:Post-processing milling + setting:milling_post_process + setting:milling_extra_size + setting:milling_after_z + setting:milling_speed +group:Post-processing script setting:full_width:height$5:post_process page:Notes:note diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 67e6d5f67..b3039f710 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -121,6 +121,8 @@ add_library(libslic3r STATIC Line.hpp MedialAxis.cpp MedialAxis.hpp + Milling/MillingPostProcess.cpp + Milling/MillingPostProcess.hpp Model.cpp Model.hpp CustomGCode.cpp diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 16dcc9fac..488c07326 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -66,6 +66,9 @@ enum OptionCategory : int wipe, hollowing, + + milling_extruders, + milling, }; std::string toString(OptionCategory opt); @@ -157,6 +160,10 @@ enum PrinterTechnology : uint8_t ptSLA = 1 << 1, // Selective Laser-Sintering ptSLS = 1 << 2, + // CNC + ptMill = 1 << 3, + // Laser engraving + ptLaser = 1 << 4, // Any technology, useful for parameters compatible with both ptFFF and ptSLA ptAny = 1+2+4, // Unknown, useful for command line processing @@ -774,7 +781,8 @@ class ConfigOptionStrings : public ConfigOptionVector { public: ConfigOptionStrings() : ConfigOptionVector() {} - explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionStrings(const std::string& value) : ConfigOptionVector(value) {} + explicit ConfigOptionStrings(size_t n, const std::string& value) : ConfigOptionVector(n, value) {} explicit ConfigOptionStrings(const std::vector &values) : ConfigOptionVector(values) {} explicit ConfigOptionStrings(std::vector &&values) : ConfigOptionVector(std::move(values)) {} explicit ConfigOptionStrings(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} @@ -990,7 +998,8 @@ class ConfigOptionPoints : public ConfigOptionVector { public: ConfigOptionPoints() : ConfigOptionVector() {} - explicit ConfigOptionPoints(size_t n, const Vec2d &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionPoints(const Vec2d& value) : ConfigOptionVector(value) {} + explicit ConfigOptionPoints(size_t n, const Vec2d& value) : ConfigOptionVector(n, value) {} explicit ConfigOptionPoints(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} explicit ConfigOptionPoints(const std::vector &values) : ConfigOptionVector(values) {} diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp index 7c505c978..edd921fb8 100644 --- a/src/libslic3r/CustomGCode.cpp +++ b/src/libslic3r/CustomGCode.cpp @@ -56,13 +56,13 @@ extern void check_mode_for_custom_gcode_per_print_z(Info& info) // Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. // print_z corresponds to the first layer printed with the new extruder. -std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders) +std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders) { - std::vector> custom_tool_changes; + std::vector> custom_tool_changes; for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes) if (custom_gcode.gcode == ToolChangeCode) { // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders - custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder)); + custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder)); } return custom_tool_changes; } diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp index a5ef1cc2e..97d874ecb 100644 --- a/src/libslic3r/CustomGCode.hpp +++ b/src/libslic3r/CustomGCode.hpp @@ -81,7 +81,7 @@ extern void check_mode_for_custom_gcode_per_print_z(Info& info); // Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. // print_z corresponds to the first layer printed with the new extruder. -std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders); +std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders); } // namespace CustomGCode diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index f7a5c5007..20ce3f3dd 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -3,19 +3,31 @@ namespace Slic3r { -Extruder::Extruder(unsigned int id, GCodeConfig *config) : +Tool::Tool(uint16_t id, GCodeConfig* config) : m_id(id), m_config(config) { reset(); - +} + +Extruder::Extruder(uint16_t id, GCodeConfig* config) : + Tool(id, config) +{ + // cache values that are going to be called often m_e_per_mm3 = this->extrusion_multiplier(); - if (! m_config->use_volumetric_e) + if (!m_config->use_volumetric_e) m_e_per_mm3 /= this->filament_crossection(); } -double Extruder::extrude(double dE) +Mill::Mill(uint16_t mill_id, GCodeConfig* config) : + Tool(mill_id, config) +{ + m_mill_id = mill_id; + m_id = mill_id + (uint16_t)config->retract_length.values.size(); +} + +double Tool::extrude(double dE) { // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) @@ -34,7 +46,7 @@ double Extruder::extrude(double dE) The restart_extra argument sets the extra length to be used for unretraction. If we're actually performing a retraction, any restart_extra value supplied will overwrite the previous one if any. */ -double Extruder::retract(double length, double restart_extra) +double Tool::retract(double length, double restart_extra) { // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) @@ -49,7 +61,7 @@ double Extruder::retract(double length, double restart_extra) return to_retract; } -double Extruder::unretract() +double Tool::unretract() { double dE = m_retracted + m_restart_extra; this->extrude(dE); @@ -59,7 +71,7 @@ double Extruder::unretract() } // Used filament volume in mm^3. -double Extruder::extruded_volume() const +double Tool::extruded_volume() const { return m_config->use_volumetric_e ? m_absolute_E + m_retracted : @@ -67,13 +79,74 @@ double Extruder::extruded_volume() const } // Used filament length in mm. -double Extruder::used_filament() const +double Tool::used_filament() const { return m_config->use_volumetric_e ? this->extruded_volume() / this->filament_crossection() : m_absolute_E + m_retracted; } +double Tool::filament_diameter() const +{ + return 0; +} + +double Tool::filament_density() const +{ + return 0; +} + +double Tool::filament_cost() const +{ + return 0; +} + +double Tool::extrusion_multiplier() const +{ + return 0; +} + +// Return a "retract_before_wipe" percentage as a factor clamped to <0, 1> +double Tool::retract_before_wipe() const +{ + return 0; +} + +double Tool::retract_length() const +{ + return 0; +} + +double Tool::retract_lift() const +{ + return 0; +} + +int Tool::retract_speed() const +{ + return 0; +} + +int Tool::deretract_speed() const +{ + return 0; +} + +double Tool::retract_restart_extra() const +{ + return 0; +} + +double Tool::retract_length_toolchange() const +{ + return 0; +} + +double Tool::retract_restart_extra_toolchange() const +{ + return 0; +} + double Extruder::filament_diameter() const { return m_config->filament_diameter.get_at(m_id); @@ -136,4 +209,8 @@ double Extruder::retract_restart_extra_toolchange() const return m_config->retract_restart_extra_toolchange.get_at(m_id); } +double Mill::retract_lift() const { + return m_config->milling_z_lift.get_at(m_mill_id); +} + } diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp index e9c6927f8..d57acc7cd 100644 --- a/src/libslic3r/Extruder.hpp +++ b/src/libslic3r/Extruder.hpp @@ -8,11 +8,11 @@ namespace Slic3r { class GCodeConfig; -class Extruder +class Tool { public: - Extruder(unsigned int id, GCodeConfig *config); - virtual ~Extruder() {} + Tool(uint16_t id, GCodeConfig *config); + virtual ~Tool() {} void reset() { m_E = 0; @@ -21,42 +21,42 @@ public: m_restart_extra = 0; } - unsigned int id() const { return m_id; } + uint16_t id() const { return m_id; } - double extrude(double dE); - double retract(double length, double restart_extra); - double unretract(); + virtual double extrude(double dE); + virtual double retract(double length, double restart_extra); + virtual double unretract(); double E() const { return m_E; } void reset_E() { m_E = 0.; } double e_per_mm(double mm3_per_mm) const { return mm3_per_mm * m_e_per_mm3; } double e_per_mm3() const { return m_e_per_mm3; } // Used filament volume in mm^3. - double extruded_volume() const; + virtual double extruded_volume() const; // Used filament length in mm. - double used_filament() const; + virtual double used_filament() const; - double filament_diameter() const; + virtual double filament_diameter() const; double filament_crossection() const { return this->filament_diameter() * this->filament_diameter() * 0.25 * PI; } - double filament_density() const; - double filament_cost() const; - double extrusion_multiplier() const; - double retract_before_wipe() const; - double retract_length() const; - double retract_lift() const; - int retract_speed() const; - int deretract_speed() const; - double retract_restart_extra() const; - double retract_length_toolchange() const; - double retract_restart_extra_toolchange() const; - -private: + virtual double filament_density() const; + virtual double filament_cost() const; + virtual double extrusion_multiplier() const; + virtual double retract_before_wipe() const; + virtual double retract_length() const; + virtual double retract_lift() const; + virtual int retract_speed() const; + virtual int deretract_speed() const; + virtual double retract_restart_extra() const; + virtual double retract_length_toolchange() const; + virtual double retract_restart_extra_toolchange() const; + +protected: // Private constructor to create a key for a search in std::set. - Extruder(unsigned int id) : m_id(id) {} + Tool(uint16_t id) : m_id(id) {} // Reference to GCodeWriter instance owned by GCodeWriter. GCodeConfig *m_config; // Print-wide global ID of this extruder. - unsigned int m_id; + uint16_t m_id; // Current state of the extruder axis, may be resetted if use_relative_e_distances. double m_E; // Current state of the extruder tachometer, used to output the extruded_volume() and used_filament() statistics. @@ -68,11 +68,55 @@ private: double m_e_per_mm3; }; + +class Mill : public Tool +{ +public: + Mill(uint16_t mill_id, GCodeConfig* config); + virtual ~Mill() {} + double retract_lift() const override; + + uint16_t mill_id() const { return m_mill_id; } + +protected: + // Private constructor to create a key for a search in std::set. + Mill(uint16_t tool_id) : Tool(tool_id) {} + uint16_t m_mill_id; +}; + +class Extruder : public Tool +{ +public: + Extruder(uint16_t id, GCodeConfig* config); + virtual ~Extruder() {} + + double filament_diameter() const override; + double filament_density() const override; + double filament_cost() const override; + double extrusion_multiplier() const override; + double retract_before_wipe() const override; + double retract_length() const override; + double retract_lift() const override; + int retract_speed() const override; + int deretract_speed() const override; + double retract_restart_extra() const override; + double retract_length_toolchange() const override; + double retract_restart_extra_toolchange() const override; + +protected: + // Private constructor to create a key for a search in std::set. + Extruder(uint16_t id) : Tool(id) {} +}; + // Sort Extruder objects by the extruder id by default. -inline bool operator==(const Extruder &e1, const Extruder &e2) { return e1.id() == e2.id(); } -inline bool operator!=(const Extruder &e1, const Extruder &e2) { return e1.id() != e2.id(); } -inline bool operator< (const Extruder &e1, const Extruder &e2) { return e1.id() < e2.id(); } -inline bool operator> (const Extruder &e1, const Extruder &e2) { return e1.id() > e2.id(); } +inline bool operator==(const Tool& e1, const Tool& e2) { return e1.id() == e2.id(); } +inline bool operator!=(const Tool& e1, const Tool& e2) { return e1.id() != e2.id(); } +inline bool operator< (const Tool& e1, const Tool& e2) { return e1.id() < e2.id(); } +inline bool operator> (const Tool& e1, const Tool& e2) { return e1.id() > e2.id(); } +inline bool operator==(const Extruder& e1, const Extruder& e2) { return e1.id() == e2.id(); } +inline bool operator!=(const Extruder& e1, const Extruder& e2) { return e1.id() != e2.id(); } +inline bool operator< (const Extruder& e1, const Extruder& e2) { return e1.id() < e2.id(); } +inline bool operator> (const Extruder& e1, const Extruder& e2) { return e1.id() > e2.id(); } } diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 703ca1f5d..d93620e05 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -280,6 +280,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role) case erSupportMaterial : return L("Support material"); case erSupportMaterialInterface : return L("Support material interface"); case erWipeTower : return L("Wipe tower"); + case erMilling : return L("Mill"); case erCustom : return L("Custom"); case erMixed : return L("Mixed"); default : assert(false); diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index a6441ccd1..3ef577a9a 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -29,6 +29,7 @@ enum ExtrusionRole : uint8_t { erSupportMaterial, erSupportMaterialInterface, erWipeTower, + erMilling, erCustom, // Extrusion role for a collection with multiple extrusion roles. erMixed, @@ -175,9 +176,9 @@ public: Polyline polyline; // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. double mm3_per_mm; - // Width of the extrusion, used for visualization purposes. + // Width of the extrusion, used for visualization purposes. Unscaled float width; - // Height of the extrusion, used for visualization purposes. + // Height of the extrusion, used for visualization purposes. Unscaled float height; ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c93303257..495c32bfd 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -91,7 +91,7 @@ NEXT: ; } void AvoidCrossingPerimeters::init_external_mp(const Print &print) -{ +{ m_external_mp = Slic3r::make_unique(union_ex(this->collect_contours_all_layers(print.objects()))); } @@ -198,10 +198,10 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen) "move to standby position"); } - if (gcodegen.config().standby_temperature_delta.value != 0) { + if (gcodegen.config().standby_temperature_delta.value != 0 && gcodegen.writer().tool_is_extruder()) { // we assume that heating is always slower than cooling, so no need to block gcode += gcodegen.writer().set_temperature - (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id()); + (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().tool()->id()); } return gcode; @@ -209,17 +209,20 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen) std::string OozePrevention::post_toolchange(GCode &gcodegen) { - return (gcodegen.config().standby_temperature_delta.value != 0) ? - gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) : + return (gcodegen.config().standby_temperature_delta.value != 0 && gcodegen.writer().tool_is_extruder()) ? + gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().tool()->id()) : std::string(); } int OozePrevention::_get_temp(GCode &gcodegen) { - return (gcodegen.layer() != NULL && gcodegen.layer()->id() == 0) - ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) - : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); + if (gcodegen.writer().tool_is_extruder()) + return (gcodegen.layer() != NULL && gcodegen.layer()->id() == 0) + ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().tool()->id()) + : gcodegen.config().temperature.get_at(gcodegen.writer().tool()->id()); + else + return 0; } std::string Wipe::wipe(GCode &gcodegen, bool toolchange) @@ -232,16 +235,16 @@ std::string Wipe::wipe(GCode &gcodegen, bool toolchange) // get the retraction length double length = toolchange - ? gcodegen.writer().extruder()->retract_length_toolchange() - : gcodegen.writer().extruder()->retract_length(); + ? gcodegen.writer().tool()->retract_length_toolchange() + : gcodegen.writer().tool()->retract_length(); // Shorten the retraction length by the amount already retracted before wipe. - length *= (1. - gcodegen.writer().extruder()->retract_before_wipe()); + length *= (1. - gcodegen.writer().tool()->retract_before_wipe()); - if (length > 0) { + if (length > 0 && gcodegen.writer().tool()->retract_speed() > 0) { /* Calculate how long we need to travel in order to consume the required amount of retraction. In other words, how far do we move in XY at wipe_speed for the time needed to consume retract_length at retract_speed? */ - double wipe_dist = scale_(length / gcodegen.writer().extruder()->retract_speed() * wipe_speed); + double wipe_dist = scale_(length / gcodegen.writer().tool()->retract_speed() * wipe_speed); /* Take the stored wipe path and replace first point with the current actual position (they might be different, for example, in case of loop clipping). */ @@ -345,13 +348,13 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // Process the end filament gcode. std::string end_filament_gcode_str; - if (gcodegen.writer().extruder() != nullptr) { + if (gcodegen.writer().tool() != nullptr && gcodegen.writer().tool_is_extruder()) { // Process the custom end_filament_gcode in case of single_extruder_multi_material. - unsigned int old_extruder_id = gcodegen.writer().extruder()->id(); + unsigned int old_extruder_id = gcodegen.writer().tool()->id(); const std::string &end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id); - if (gcodegen.writer().extruder() != nullptr && ! end_filament_gcode.empty()) { + if (gcodegen.writer().tool() != nullptr && ! end_filament_gcode.empty()) { DynamicConfig config; - int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; + int previous_extruder_id = gcodegen.writer().tool() ? (int)gcodegen.writer().tool()->id() : -1; config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); @@ -368,7 +371,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; if (!toolchange_gcode.empty()) { DynamicConfig config; - int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; + int previous_extruder_id = gcodegen.writer().tool() ? (int)gcodegen.writer().tool()->id() : -1; config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); @@ -396,7 +399,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // Process the start_filament_gcode for the active filament only. DynamicConfig config; config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); - config.set_key_value("previous_extruder", new ConfigOptionInt(gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1)); + config.set_key_value("previous_extruder", new ConfigOptionInt(gcodegen.writer().tool() ? (int)gcodegen.writer().tool()->id() : -1)); config.set_key_value("next_extruder", new ConfigOptionInt(new_extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(0)); config.set_key_value("layer_z", new ConfigOptionFloat(z)); @@ -598,6 +601,8 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen) } #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) +#define EXTRUDER_CONFIG_WITH_DEFAULT(OPT,DEF) (m_writer.tool_is_extruder()?m_config.OPT.get_at(m_writer.tool()->id()):DEF) +#define BOOL_EXTRUDER_CONFIG(OPT) m_writer.tool_is_extruder() && m_config.OPT.get_at(m_writer.tool()->id()) // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. @@ -905,12 +910,12 @@ namespace DoExport { // send extruder offset data to analyzer GCodeAnalyzer::ExtruderOffsetsMap extruder_offsets; unsigned int num_extruders = static_cast(config.nozzle_diameter.values.size()); - for (unsigned int extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) - { - Vec2d offset = config.extruder_offset.get_at(extruder_id); - if (!offset.isApprox(Vec2d::Zero())) - extruder_offsets[extruder_id] = offset; - } + for (unsigned int extruder_id = 0; extruder_id < num_extruders; ++extruder_id) + { + Vec2d offset = config.extruder_offset.get_at(extruder_id); + if (!offset.isApprox(Vec2d::Zero())) + extruder_offsets[extruder_id] = offset; + } analyzer.set_extruder_offsets(extruder_offsets); // tell analyzer about the extrusion axis @@ -1330,6 +1335,19 @@ void GCode::_do_export(Print &print, FILE *file) bool has_wipe_tower = false; std::vector print_object_instances_ordering; std::vector::const_iterator print_object_instance_sequential_active; + bool has_milling = false; + if (!config().milling_diameter.values.empty()) { + for (const PrintObject* obj : print.objects()) { + for (const Layer *layer : obj->layers()) { + for (const LayerRegion *lr : layer->regions()) { + if (!lr->milling.empty()) { + has_milling = true; + break; + } + } + } + } + } if (print.config().complete_objects.value) { // Order object instances for sequential print. if(print.config().complete_objects_sort.value == cosObject) @@ -1348,6 +1366,8 @@ void GCode::_do_export(Print &print, FILE *file) // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. // Use the extruder IDs collected from Regions. this->set_extruders(print.extruders()); + if(has_milling) + m_writer.set_mills(std::vector() = { 0 }); } else { // Find tool ordering for all the objects at once, and the initial extruder ID. // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. @@ -1362,6 +1382,8 @@ void GCode::_do_export(Print &print, FILE *file) // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z. // Therefore initialize the printing extruders from there. this->set_extruders(tool_ordering.all_extruders()); + if (has_milling) + m_writer.set_mills(std::vector() = { 0 }); // Order object instances using a nearest neighbor search. print_object_instances_ordering = chain_print_object_instances(print); } @@ -1566,22 +1588,25 @@ void GCode::_do_export(Print &print, FILE *file) { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); - if (print.config().single_extruder_multi_material) { - // Process the end_filament_gcode for the active filament only. - int extruder_id = m_writer.extruder()->id(); - config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); - _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config)); - } else { - for (const std::string &end_gcode : print.config().end_filament_gcode.values) { - int extruder_id = (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); + config.set_key_value("current_extruder_id", new ConfigOptionInt((int)m_writer.tool()->id())); + if (m_writer.tool_is_extruder()) { + if (print.config().single_extruder_multi_material) { + // Process the end_filament_gcode for the active filament only. + int extruder_id = m_writer.tool()->id(); config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); - config.set_key_value("previous_extruder", new ConfigOptionInt(extruder_id)); - config.set_key_value("next_extruder", new ConfigOptionInt(0)); - _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config)); + _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config)); + } else { + for (const std::string& end_gcode : print.config().end_filament_gcode.values) { + int extruder_id = (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()); + config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); + config.set_key_value("previous_extruder", new ConfigOptionInt(extruder_id)); + config.set_key_value("next_extruder", new ConfigOptionInt(0)); + _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config)); + } } } - _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config)); + _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.tool()->id(), &config)); } _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% _write(file, m_writer.postamble()); @@ -1889,17 +1914,17 @@ std::string GCode::emit_custom_gcode_per_print_z( //update stats : weight double previously_extruded = 0; for (const auto& tuple : stats.color_extruderid_to_used_weight) - if (tuple.first == this->m_writer.extruder()->id()) + if (tuple.first == this->m_writer.tool()->id()) previously_extruded += tuple.second; - double extruded = this->m_writer.extruder()->filament_density() * this->m_writer.extruder()->extruded_volume(); - stats.color_extruderid_to_used_weight.emplace_back(this->m_writer.extruder()->id(), extruded - previously_extruded); + double extruded = this->m_writer.tool()->filament_density() * this->m_writer.tool()->extruded_volume(); + stats.color_extruderid_to_used_weight.emplace_back(this->m_writer.tool()->id(), extruded - previously_extruded); //update stats : length previously_extruded = 0; for (const auto& tuple : stats.color_extruderid_to_used_filament) - if (tuple.first == this->m_writer.extruder()->id()) + if (tuple.first == this->m_writer.tool()->id()) previously_extruded += tuple.second; - stats.color_extruderid_to_used_filament.emplace_back(this->m_writer.extruder()->id(), this->m_writer.extruder()->used_filament() - previously_extruded); + stats.color_extruderid_to_used_filament.emplace_back(this->m_writer.tool()->id(), this->m_writer.tool()->used_filament() - previously_extruded); } // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count @@ -2085,7 +2110,7 @@ void GCode::process_layer( config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); gcode += this->placeholder_parser_process("before_layer_gcode", - print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config) + print.config().before_layer_gcode.value, m_writer.tool()->id(), &config) + "\n"; } gcode += this->change_layer(print_z); // this will increase m_layer_index @@ -2095,7 +2120,7 @@ void GCode::process_layer( config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); gcode += this->placeholder_parser_process("layer_gcode", - print.config().layer_gcode.value, m_writer.extruder()->id(), &config) + print.config().layer_gcode.value, m_writer.tool()->id(), &config) + "\n"; } @@ -2103,7 +2128,7 @@ void GCode::process_layer( // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent // first_layer_temperature vs. temperature settings. for (const Extruder &extruder : m_writer.extruders()) { - if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id()) + if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.tool()->id()) // In single extruder multi material mode, set the temperature for the current extruder only. continue; int temperature = print.config().temperature.get_at(extruder.id()); @@ -2434,6 +2459,81 @@ void GCode::process_layer( if (m_spiral_vase) gcode = m_spiral_vase->process_layer(gcode); + + //add milling post-process if enabled + if (!config().milling_diameter.values.empty()) { + bool milling_ok = false; + for (const LayerToPrint& ltp : layers) { + if (ltp.object_layer != nullptr) { + for (const LayerRegion* lr : ltp.object_layer->regions()) { + if (!lr->milling.empty()) { + milling_ok = true; + break; + } + } + } + } + if (milling_ok) { + //switch to mill + gcode += "; milling ok\n"; + uint32_t current_extruder_filament = m_writer.tool()->id(); + uint32_t milling_extruder_id = config().nozzle_diameter.values.size(); + gcode += "; toolchange:\n"; + gcode += m_writer.toolchange(milling_extruder_id); + gcode += "; toolchange done\n"; + m_placeholder_parser.set("current_extruder", milling_extruder_id); + // Append the filament start G-code. + const std::string& start_mill_gcode = m_config.milling_toolchange_start_gcode.get_at(0); + if (!start_mill_gcode.empty()) { + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)current_extruder_filament)); + config.set_key_value("next_extruder", new ConfigOptionInt((int)milling_extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + // Process the start_mill_gcode for the new filament. + gcode += this->placeholder_parser_process("milling_toolchange_start_gcode", start_mill_gcode, current_extruder_filament, &config); + check_add_eol(gcode); + } + + gcode += "\n; began print:"; + for (const LayerToPrint& ltp : layers) { + if (ltp.object_layer != nullptr) { + for (const PrintInstance& print_instance : ltp.object()->instances()){ + this->set_origin(unscale(print_instance.shift)); + for (const LayerRegion* lr : ltp.object_layer->regions()) { + if (!lr->milling.empty()) { + //EXTRUDE MOVES + gcode += "; extrude lr->milling\n"; + gcode += this->extrude_entity(lr->milling, "; milling post-process"); + } + } + } + } + } + + //switch to extruder + m_placeholder_parser.set("current_extruder", milling_extruder_id); + // Append the filament start G-code. + const std::string& end_mill_gcode = m_config.milling_toolchange_end_gcode.get_at(0); + if (!end_mill_gcode.empty()) { + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)milling_extruder_id)); + config.set_key_value("next_extruder", new ConfigOptionInt((int)current_extruder_filament)); + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + // Process the end_mill_gcode for the new filament. + gcode += this->placeholder_parser_process("milling_toolchange_start_gcode", end_mill_gcode, current_extruder_filament, &config); + check_add_eol(gcode); + } + gcode += "; will go back to normal extruder\n"; + //TODO: change wipetower code to add an other filament change per layer. + gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ? + m_wipe_tower->tool_change(*this, current_extruder_filament, current_extruder_filament == layer_tools.extruders.back()) : + this->set_extruder(current_extruder_filament, print_z); + } + } + + // Apply cooling logic; this may alter speeds. if (m_cooling_buffer) gcode = m_cooling_buffer->process_layer(gcode, layer.id()); @@ -2487,10 +2587,10 @@ void GCode::append_full_config(const Print &print, std::string &str) str += "; " + key + " = " + cfg.opt_serialize(key) + "\n"; } -void GCode::set_extruders(const std::vector &extruder_ids) +void GCode::set_extruders(const std::vector& extruder_ids) { m_writer.set_extruders(extruder_ids); - + // enable wipe path generation if any extruder has wipe enabled m_wipe.enable = false; for (auto id : extruder_ids) @@ -2533,7 +2633,7 @@ std::string GCode::change_layer(coordf_t print_z) // Increment a progress bar indicator. gcode += m_writer.update_progress(++ m_layer_index, m_layer_count); coordf_t z = print_z + m_config.z_offset.value; // in unscaled coordinates - if (EXTRUDER_CONFIG(retract_layer_change) && m_writer.will_move_z(z)) + if (BOOL_EXTRUDER_CONFIG(retract_layer_change) && m_writer.will_move_z(z)) gcode += this->retract(); { @@ -2756,7 +2856,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case double clip_length = m_enable_loop_clipping ? - scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER : + scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER : 0; // get paths @@ -2776,7 +2876,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s } //all in unscaled coordinates (hence why it's coordf_t and not coord_t) - const coordf_t min_height = EXTRUDER_CONFIG(min_layer_height); + const coordf_t min_height = EXTRUDER_CONFIG_WITH_DEFAULT(min_layer_height, this->m_layer->height); const coordf_t bot_init_z = - this->m_layer->height; //const coordf_t bot_last_z = bot_init_z + this->m_layer->height - EXTRUDER_CONFIG(min_layer_height); const coordf_t init_z = bot_init_z + min_height; @@ -2807,7 +2907,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s Vec2d p1 = paths.front().polyline.points.front().cast(); Vec2d p2 = paths.front().polyline.points[1].cast(); Vec2d v = p2 - p1; - double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); + double nd = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, paths.front().width)); double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -2856,7 +2956,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s // calculate extrusion length per distance unit double e_per_mm_per_height = (path->mm3_per_mm / this->m_layer->height) - * m_writer.extruder()->e_per_mm3() + * m_writer.tool()->e_per_mm3() * this->config().print_extrusion_multiplier.get_abs_value(1); if (m_writer.extrusion_axis().empty()) e_per_mm_per_height = 0; { @@ -2864,7 +2964,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s for (const Line &line : path->polyline.lines()) { const coordf_t line_length = line.length() * SCALING_FACTOR; //don't go (much) more than a nozzle_size without a refresh of the z & extrusion rate - const int nb_sections = std::max(1,int(line_length / EXTRUDER_CONFIG(nozzle_diameter))); + const int nb_sections = std::max(1,int(line_length / EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, paths.front().width))); const coordf_t height_increment = height_per_length * line_length / nb_sections; Vec3d last_point{ this->point_to_gcode(line.a).x(), this->point_to_gcode(line.a).y(), current_z }; const Vec3d pos_increment{ (this->point_to_gcode(line.b).x() - last_point.x()) / nb_sections, @@ -2955,7 +3055,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s Vec2d p1 = paths.front().polyline.points.front().cast(); Vec2d p2 = paths.front().polyline.points[1].cast(); Vec2d v = p2 - p1; - double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); + double nd = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, paths.front().width)); double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -2971,6 +3071,8 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptr *lower_layer_edge_grid) { + if (loop.paths.empty()) + return; SeamPosition seam_position = m_config.seam_position; if (loop.loop_role() == elrSkirt) @@ -2983,7 +3085,7 @@ void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptrm_layer->id() > 0 //exclude if min_layer_height * 2 > layer_height (increase from 2 to 3 because it's working but uses in-between) - && this->m_layer->height >= EXTRUDER_CONFIG(min_layer_height) * 2 - EPSILON + && this->m_layer->height >= EXTRUDER_CONFIG_WITH_DEFAULT(min_layer_height, 0) * 2 - EPSILON ) { return extrude_loop_vase(original_loop, description, speed, lower_layer_edge_grid); } @@ -3224,7 +3326,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case double clip_length = m_enable_loop_clipping ? - scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER : + scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER : 0; // get paths @@ -3260,8 +3362,8 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s Point next_point = paths.front().polyline.points[1]; // second point //extra wipe before the little move. - if (EXTRUDER_CONFIG(wipe_extra_perimeter) > 0) { - coord_t wipe_dist = scale_(EXTRUDER_CONFIG(wipe_extra_perimeter)); + if (EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter, 0) > 0) { + coord_t wipe_dist = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter,0)); ExtrusionPaths paths_wipe; for (int i = 0; i < paths.size(); i++) { ExtrusionPath& path = paths[i]; @@ -3319,7 +3421,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s Vec2d current_pos = current_point.cast(); Vec2d next_pos = next_point.cast(); Vec2d vec_dist = next_pos - current_pos; - double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); + double nd = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)); double l2 = vec_dist.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -3359,7 +3461,7 @@ std::string GCode::extrude_multi_path3D(const ExtrusionMultiPath3D &multipath3D, // calculate extrusion length per distance unit double e_per_mm = path.mm3_per_mm - * m_writer.extruder()->e_per_mm3() + * m_writer.tool()->e_per_mm3() * this->config().print_extrusion_multiplier.get_abs_value(1); if (m_writer.extrusion_axis().empty()) e_per_mm = 0; double path_length = 0.; @@ -3428,7 +3530,7 @@ std::string GCode::extrude_path_3D(const ExtrusionPath3D &path, const std::strin // calculate extrusion length per distance unit double e_per_mm = path.mm3_per_mm - * m_writer.extruder()->e_per_mm3() + * m_writer.tool()->e_per_mm3() * this->config().print_extrusion_multiplier.get_abs_value(1); if (m_writer.extrusion_axis().empty()) e_per_mm = 0; double path_length = 0.; @@ -3589,7 +3691,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri // calculate extrusion length per distance unit double e_per_mm = path.mm3_per_mm - * m_writer.extruder()->e_per_mm3() + * m_writer.tool()->e_per_mm3() * this->config().print_extrusion_multiplier.get_abs_value(1); if (this->m_layer_index <= 0) e_per_mm *= this->config().first_layer_flow_ratio.get_abs_value(1); if (m_writer.extrusion_axis().empty()) e_per_mm = 0; @@ -3724,6 +3826,8 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string speed = m_config.get_abs_value("gap_fill_speed"); } else if (path.role() == erNone) { speed = m_config.get_abs_value("travel_speed"); + } else if (path.role() == erMilling) { + speed = m_config.get_abs_value("milling_speed"); } else { throw std::invalid_argument("Invalid speed"); } @@ -3742,11 +3846,11 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string m_config.max_volumetric_speed.value / path.mm3_per_mm ); } - if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { + if (EXTRUDER_CONFIG_WITH_DEFAULT(filament_max_volumetric_speed,0) > 0) { // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) speed = std::min( speed, - EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm + EXTRUDER_CONFIG_WITH_DEFAULT(filament_max_volumetric_speed, speed) / path.mm3_per_mm ); } double F = speed * 60; // convert mm/sec to mm/min @@ -3758,7 +3862,7 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); config.set_key_value("layer_z", new ConfigOptionFloat(m_config.z_offset.value)); gcode += this->placeholder_parser_process("feature_gcode", - m_config.feature_gcode.value, m_writer.extruder()->id(), &config) + m_config.feature_gcode.value, m_writer.tool()->id(), &config) + "\n"; } if (m_enable_extrusion_role_markers) { @@ -3891,7 +3995,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) { - if (travel.length() < scale_(EXTRUDER_CONFIG(retract_before_travel))) { + if (travel.length() < scale_(EXTRUDER_CONFIG_WITH_DEFAULT(retract_before_travel, 0))) { // skip retraction if the move is shorter than the configured threshold return false; } @@ -3921,7 +4025,7 @@ std::string GCode::retract(bool toolchange) { std::string gcode; - if (m_writer.extruder() == nullptr) + if (m_writer.tool() == nullptr) return gcode; // We need to reset e before any extrusion or wipe to allow the reset to happen at the real @@ -3929,7 +4033,7 @@ std::string GCode::retract(bool toolchange) gcode += m_writer.reset_e(); // wipe (if it's enabled for this extruder and we have a stored wipe path) - if (EXTRUDER_CONFIG(wipe) && m_wipe.has_path()) { + if (BOOL_EXTRUDER_CONFIG(wipe) && m_wipe.has_path()) { gcode += toolchange ? m_writer.retract_for_toolchange(true) : m_writer.retract(true); gcode += m_wipe.wipe(*this, toolchange); } @@ -3939,8 +4043,14 @@ std::string GCode::retract(bool toolchange) methods even if we performed wipe, since this will ensure the entire retraction length is honored in case wipe path was too short. */ gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); - if (toolchange || !this->m_config.retract_lift_not_last_layer.get_at(m_writer.extruder()->id()) || !(this->m_last_extrusion_role == ExtrusionRole::erTopSolidInfill)) - if (m_writer.extruder()->retract_length() > 0 || m_config.use_firmware_retraction) + if (toolchange + || !(BOOL_EXTRUDER_CONFIG(retract_lift_not_last_layer) && (this->m_last_extrusion_role == ExtrusionRole::erTopSolidInfill )) + || !m_writer.tool_is_extruder() + ) + if (m_writer.tool()->retract_length() > 0 + || m_config.use_firmware_retraction + || (!m_writer.tool_is_extruder() && m_writer.tool()->retract_lift() != 0) + ) gcode += m_writer.lift(); return gcode; @@ -3978,14 +4088,14 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Always reset the extrusion path, even if the tool change retract is set to zero. m_wipe.reset_path(); - if (m_writer.extruder() != nullptr) { + if (m_writer.tool() != nullptr) { // Process the custom end_filament_gcode. set_extruder() is only called if there is no wipe tower // so it should not be injected twice. - unsigned int old_extruder_id = m_writer.extruder()->id(); + unsigned int old_extruder_id = m_writer.tool()->id(); const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id); if (! end_filament_gcode.empty()) { DynamicConfig config; - config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.extruder() != nullptr ? m_writer.extruder()->id() : -1))); + config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.tool() != nullptr ? m_writer.tool()->id() : -1))); config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); @@ -3997,7 +4107,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // If ooze prevention is enabled, park current extruder in the nearest // standby point and set it to the standby temperature. - if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) + if (m_ooze_prevention.enable && m_writer.tool() != nullptr) gcode += m_ooze_prevention.pre_toolchange(*this); const std::string& toolchange_gcode = m_config.toolchange_gcode.value; @@ -4006,7 +4116,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Process the custom toolchange_gcode. If it is empty, insert just a Tn command. if (!toolchange_gcode.empty()) { DynamicConfig config; - config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.extruder() != nullptr ? m_writer.extruder()->id() : -1 ))); + config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.tool() != nullptr ? m_writer.tool()->id() : -1 ))); config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); @@ -4037,7 +4147,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); if (! start_filament_gcode.empty()) { DynamicConfig config; - config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.extruder() != nullptr ? m_writer.extruder()->id() : -1))); + config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.tool() != nullptr ? m_writer.tool()->id() : -1))); config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); @@ -4055,13 +4165,13 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // convert a model-space scaled point into G-code coordinates Vec2d GCode::point_to_gcode(const Point &point) const { - Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); + Vec2d extruder_offset = EXTRUDER_CONFIG_WITH_DEFAULT(extruder_offset, Vec2d(0, 0)); //FIXME : mill ofsset return unscale(point) + m_origin - extruder_offset; } // convert a model-space scaled point into G-code coordinates Vec3d GCode::point_to_gcode(const Point &point, const coord_t z_offset) const { - Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); + Vec2d extruder_offset = EXTRUDER_CONFIG_WITH_DEFAULT(extruder_offset, Vec2d(0, 0)); //FIXME : mill ofsset Vec3d ret_vec(unscale_(point.x()) + m_origin.x() - extruder_offset.x(), unscale_(point.y()) + m_origin.y() - extruder_offset.y(), unscale_(z_offset)); @@ -4071,7 +4181,7 @@ Vec3d GCode::point_to_gcode(const Point &point, const coord_t z_offset) const { // convert a model-space scaled point into G-code coordinates Point GCode::gcode_to_point(const Vec2d &point) const { - Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); + Vec2d extruder_offset = EXTRUDER_CONFIG_WITH_DEFAULT(extruder_offset, Vec2d(0, 0)); //FIXME : mill ofsset return Point( scale_(point(0) - m_origin(0) + extruder_offset(0)), scale_(point(1) - m_origin(1) + extruder_offset(1))); @@ -4205,6 +4315,8 @@ GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & role) { return "SupportMaterialInterface"; case erWipeTower: return "WipeTower"; + case erMilling: + return "Mill"; case erCustom: case erMixed: case erCount: diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 45b4e8487..68f634ff4 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -240,7 +240,7 @@ private: void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } bool last_pos_defined() const { return m_last_pos_defined; } - void set_extruders(const std::vector &extruder_ids); + void set_extruders(const std::vector &extruder_ids); std::string preamble(); std::string change_layer(coordf_t print_z); std::string visitor_gcode; diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 81d0b15a0..a088bf33e 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -294,15 +294,15 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: { const FullPrintConfig &config = m_gcodegen.config(); const std::vector &extruders = m_gcodegen.writer().extruders(); - unsigned int num_extruders = 0; + uint16_t num_extruders = 0; for (const Extruder &ex : extruders) - num_extruders = std::max(ex.id() + 1, num_extruders); + num_extruders = std::max(uint16_t(ex.id() + 1), num_extruders); std::vector per_extruder_adjustments(extruders.size()); std::vector map_extruder_to_per_extruder_adjustment(num_extruders, 0); for (size_t i = 0; i < extruders.size(); ++ i) { PerExtruderAdjustments &adj = per_extruder_adjustments[i]; - unsigned int extruder_id = extruders[i].id(); + uint16_t extruder_id = extruders[i].id(); adj.extruder_id = extruder_id; adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id); adj.slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(extruder_id)); @@ -311,7 +311,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix(); - unsigned int current_extruder = m_current_extruder; + uint16_t current_extruder = m_current_extruder; PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]]; const char *line_start = gcode.c_str(); const char *line_end = line_start; @@ -418,7 +418,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = CoolingLine::TYPE_EXTRUDE_END; active_speed_modifier = size_t(-1); } else if (boost::starts_with(sline, toolchange_prefix)) { - unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + toolchange_prefix.size()); + uint16_t new_extruder = (uint16_t)atoi(sline.c_str() + toolchange_prefix.size()); // Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored. if (new_extruder < map_extruder_to_per_extruder_adjustment.size()) { if (new_extruder != current_extruder) { diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index b7c77e9a0..014efd39d 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -124,6 +124,7 @@ const Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] Color(0.0f, 0.5f, 0.0f, 1.0f), // erSupportMaterial Color(0.0f, 0.0f, 0.5f, 1.0f), // erSupportMaterialInterface Color(0.7f, 0.89f, 0.67f, 1.0f), // erWipeTower + Color(0.7f, 0.7, 0.7, 1.0f), // erMilling Color(1.0f, 1.0f, 0.0f, 1.0f), // erCustom Color(0.0f, 0.0f, 0.0f, 1.0f) // erMixed }; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index a77da4966..c9ce9c941 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -21,7 +21,7 @@ namespace Slic3r { // Returns true in case that extruder a comes before b (b does not have to be present). False otherwise. -bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const +bool LayerTools::is_extruder_order(uint16_t a, uint16_t b) const { if (a == b) return false; @@ -37,32 +37,32 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const } // Return a zero based extruder from the region, or extruder_override if overriden. -unsigned int LayerTools::perimeter_extruder(const PrintRegion ®ion) const +uint16_t LayerTools::perimeter_extruder(const PrintRegion ®ion) const { assert(region.config().perimeter_extruder.value > 0); return ((this->extruder_override == 0) ? region.config().perimeter_extruder.value : this->extruder_override) - 1; } -unsigned int LayerTools::infill_extruder(const PrintRegion ®ion) const +uint16_t LayerTools::infill_extruder(const PrintRegion ®ion) const { assert(region.config().infill_extruder.value > 0); return ((this->extruder_override == 0) ? region.config().infill_extruder.value : this->extruder_override) - 1; } -unsigned int LayerTools::solid_infill_extruder(const PrintRegion ®ion) const +uint16_t LayerTools::solid_infill_extruder(const PrintRegion ®ion) const { assert(region.config().solid_infill_extruder.value > 0); return ((this->extruder_override == 0) ? region.config().solid_infill_extruder.value : this->extruder_override) - 1; } // Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden. -unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const +uint16_t LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const { assert(region.config().perimeter_extruder.value > 0); assert(region.config().infill_extruder.value > 0); assert(region.config().solid_infill_extruder.value > 0); // 1 based extruder ID. - unsigned int extruder = ((this->extruder_override == 0) ? + uint16_t extruder = ((this->extruder_override == 0) ? (is_infill(extrusions.role()) ? (is_solid_infill(extrusions.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) : region.config().perimeter_extruder.value) : @@ -72,7 +72,7 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c // For the use case when each object is printed separately // (print.config().complete_objects is true). -ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material) +ToolOrdering::ToolOrdering(const PrintObject &object, uint16_t first_extruder, bool prime_multi_material) { if (object.layers().empty()) return; @@ -89,7 +89,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude } // Collect extruders reuqired to print the layers. - this->collect_extruders(object, std::vector>()); + this->collect_extruders(object, std::vector>()); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); @@ -101,7 +101,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // For the use case when all objects are printed at once. // (print.config().complete_objects is false). -ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material) +ToolOrdering::ToolOrdering(const Print &print, uint16_t first_extruder, bool prime_multi_material) { m_print_config_ptr = &print.config(); @@ -128,8 +128,8 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object. // Do it only if all the objects were configured to be printed with a single extruder. - std::vector> per_layer_extruder_switches; - if (auto num_extruders = unsigned(print.config().nozzle_diameter.size()); + std::vector> per_layer_extruder_switches; + if (uint16_t num_extruders = uint16_t(print.config().nozzle_diameter.size()); num_extruders > 1 && print.object_extruders(print.objects()).size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle print.model().custom_gcode_per_print_z.mode == CustomGCode::MultiAsSingle) { // Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material). @@ -165,7 +165,7 @@ void ToolOrdering::initialize_layers(std::vector &zs) } // Collect extruders reuqired to print layers. -void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches) +void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches) { // Collect the support extruders. for (auto support_layer : object.support_layers()) { @@ -173,8 +173,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto ExtrusionRole role = support_layer->support_fills.role(); bool has_support = role == erMixed || role == erSupportMaterial; bool has_interface = role == erMixed || role == erSupportMaterialInterface; - unsigned int extruder_support = object.config().support_material_extruder.value; - unsigned int extruder_interface = object.config().support_material_interface_extruder.value; + uint16_t extruder_support = object.config().support_material_extruder.value; + uint16_t extruder_interface = object.config().support_material_interface_extruder.value; if (has_support) layer_tools.extruders.push_back(extruder_support); if (has_interface) @@ -184,9 +184,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto } // Extruder overrides are ordered by print_z. - std::vector>::const_iterator it_per_layer_extruder_override; + std::vector>::const_iterator it_per_layer_extruder_override; it_per_layer_extruder_override = per_layer_extruder_switches.begin(); - unsigned int extruder_override = 0; + uint16_t extruder_override = 0; // Collect the object extruders. for (auto layer : object.layers()) { @@ -265,18 +265,18 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto } // Reorder extruders to minimize layer changes. -void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) +void ToolOrdering::reorder_extruders(uint16_t last_extruder_id) { if (m_layer_tools.empty()) return; - if (last_extruder_id == (unsigned int)-1) { + if (last_extruder_id == (uint16_t)-1) { // The initial print extruder has not been decided yet. // Initialize the last_extruder_id with the first non-zero extruder id used for the print. last_extruder_id = 0; for (size_t i = 0; i < m_layer_tools.size() && last_extruder_id == 0; ++ i) { const LayerTools < = m_layer_tools[i]; - for (unsigned int extruder_id : lt.extruders) + for (uint16_t extruder_id : lt.extruders) if (extruder_id > 0) { last_extruder_id = extruder_id; break; @@ -302,7 +302,7 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) for (size_t i = 1; i < lt.extruders.size(); ++ i) if (lt.extruders[i] == last_extruder_id) { // Move the last extruder to the front. - memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int)); + memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(uint16_t)); lt.extruders.front() = last_extruder_id; break; } @@ -312,7 +312,7 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) // Reindex the extruders, so they are zero based, not 1 based. for (LayerTools < : m_layer_tools) - for (unsigned int &extruder_id : lt.extruders) { + for (uint16_t &extruder_id : lt.extruders) { assert(extruder_id > 0); -- extruder_id; } @@ -393,7 +393,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ // and maybe other problems. We will therefore go through layer_tools and detect and fix this. // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder), // we'll mark it with has_wipe tower. - for (unsigned int i=0; i+1 1)) lt_next.has_wipe_tower = true; // We should also check that the next wipe tower layer is no further than max_layer_height: - unsigned int j = i+1; + uint16_t j = i+1; double last_wipe_tower_print_z = lt_next.print_z; while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower) if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height) { @@ -421,14 +421,14 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material) { - m_first_printing_extruder = (unsigned int)-1; + m_first_printing_extruder = (uint16_t)-1; for (const auto < : m_layer_tools) if (! lt.extruders.empty()) { m_first_printing_extruder = lt.extruders.front(); break; } - m_last_printing_extruder = (unsigned int)-1; + m_last_printing_extruder = (uint16_t)-1; for (auto lt_it = m_layer_tools.rbegin(); lt_it != m_layer_tools.rend(); ++ lt_it) if (! lt_it->extruders.empty()) { m_last_printing_extruder = lt_it->extruders.back(); @@ -446,7 +446,7 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material) // Then set m_first_printing_extruder to the 1st extruder primed. m_all_printing_extruders.erase( std::remove_if(m_all_printing_extruders.begin(), m_all_printing_extruders.end(), - [ this ](const unsigned int eid) { return eid == m_first_printing_extruder; }), + [ this ](const uint16_t eid) { return eid == m_first_printing_extruder; }), m_all_printing_extruders.end()); m_all_printing_extruders.emplace_back(m_first_printing_extruder); m_first_printing_extruder = m_all_printing_extruders.front(); @@ -482,7 +482,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) { LayerTools < = *it_lt; // Add the extruders of the current layer to the set of extruders printing at and above this print_z. - for (unsigned int i : lt.extruders) + for (uint16_t i : lt.extruders) extruder_printing_above[i] = true; // Skip all custom G-codes above this layer and skip all extruder switches. for (; custom_gcode_it != custom_gcode_per_print_z.gcodes.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->gcode == ToolChangeCode); ++ custom_gcode_it); @@ -584,7 +584,7 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con // Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange // and returns volume that is left to be wiped on the wipe tower. -float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe) +float WipingExtrusions::mark_wiping_extrusions(const Print& print, uint16_t old_extruder, uint16_t new_extruder, float volume_to_wipe) { const LayerTools& lt = *m_layer_tools; const float min_infill_volume = 0.f; // ignore infill with smaller volume than this @@ -619,7 +619,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int size_t num_of_copies = object->instances().size(); // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves - for (unsigned int copy = 0; copy < num_of_copies; ++copy) { + for (uint16_t copy = 0; copy < num_of_copies; ++copy) { for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { const auto& region = *object->print()->regions()[region_id]; @@ -683,8 +683,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) return; const LayerTools& lt = *m_layer_tools; - unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config()); - unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config()); + uint16_t first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config()); + uint16_t last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config()); for (const PrintObject* object : print.objects()) { // Finds this layer: diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 2252270d4..d105908fe 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -35,7 +35,7 @@ public: // This function goes through all infill entities, decides which ones will be used for wiping and // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower: - float mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe); + float mark_wiping_extrusions(const Print& print, uint16_t old_extruder, uint16_t new_extruder, float volume_to_wipe); void ensure_perimeters_infills_order(const Print& print); @@ -79,24 +79,24 @@ public: bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; } bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; } - bool is_extruder_order(unsigned int a, unsigned int b) const; - bool has_extruder(unsigned int extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); } + bool is_extruder_order(uint16_t a, uint16_t b) const; + bool has_extruder(uint16_t extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); } // Return a zero based extruder from the region, or extruder_override if overriden. - unsigned int perimeter_extruder(const PrintRegion ®ion) const; - unsigned int infill_extruder(const PrintRegion ®ion) const; - unsigned int solid_infill_extruder(const PrintRegion ®ion) const; + uint16_t perimeter_extruder(const PrintRegion ®ion) const; + uint16_t infill_extruder(const PrintRegion ®ion) const; + uint16_t solid_infill_extruder(const PrintRegion ®ion) const; // Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden. - unsigned int extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const; + uint16_t extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const; coordf_t print_z = 0.; bool has_object = false; bool has_support = false; // Zero based extruder IDs, ordered to minimize tool switches. - std::vector extruders; + std::vector extruders; // If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with. // If not overriden, it is set to 0. - unsigned int extruder_override = 0; + uint16_t extruder_override = 0; // Will there be anything extruded on this layer for the wipe tower? // Due to the support layers possibly interleaving the object layers, // wipe tower will be disabled for some support only layers. @@ -127,11 +127,11 @@ public: // For the use case when each object is printed separately // (print.config.complete_objects is true). - ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material = false); + ToolOrdering(const PrintObject &object, uint16_t first_extruder, bool prime_multi_material = false); // For the use case when all objects are printed at once. // (print.config.complete_objects is false). - ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material = false); + ToolOrdering(const Print &print, uint16_t first_extruder, bool prime_multi_material = false); void clear() { m_layer_tools.clear(); } @@ -148,7 +148,7 @@ public: unsigned int last_extruder() const { return m_last_printing_extruder; } // For a multi-material print, the printing extruders are ordered in the order they shall be primed. - const std::vector& all_extruders() const { return m_all_printing_extruders; } + const std::vector& all_extruders() const { return m_all_printing_extruders; } // Find LayerTools with the closest print_z. const LayerTools& tools_for_layer(coordf_t print_z) const; @@ -160,24 +160,24 @@ public: std::vector::const_iterator end() const { return m_layer_tools.end(); } bool empty() const { return m_layer_tools.empty(); } std::vector& layer_tools() { return m_layer_tools; } - bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; } + bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (uint16_t)-1 && m_layer_tools.front().wipe_tower_partitions > 0; } private: void initialize_layers(std::vector &zs); - void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); - void reorder_extruders(unsigned int last_extruder_id); + void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); + void reorder_extruders(uint16_t last_extruder_id); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void collect_extruder_statistics(bool prime_multi_material); - std::vector m_layer_tools; + std::vector m_layer_tools; // First printing extruder, including the multi-material priming sequence. - unsigned int m_first_printing_extruder = (unsigned int)-1; + uint16_t m_first_printing_extruder = (uint16_t)-1; // Final printing extruder. - unsigned int m_last_printing_extruder = (unsigned int)-1; + uint16_t m_last_printing_extruder = (uint16_t)-1; // All extruders, which extrude some material over m_layer_tools. - std::vector m_all_printing_extruders; + std::vector m_all_printing_extruders; - const PrintConfig* m_print_config_ptr = nullptr; + const PrintConfig* m_print_config_ptr = nullptr; }; diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 9c24cc61c..78e71bd32 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -624,7 +624,7 @@ std::vector WipeTower::prime( // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. - const std::vector &tools, + const std::vector &tools, // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower. // If false, the last priming are will be large enough to wipe the last extruder sufficiently. bool /*last_wipe_inside_wipe_tower*/) @@ -672,7 +672,7 @@ std::vector WipeTower::prime( writer.set_initial_position(results.back().end_pos); - unsigned int tool = tools[idx_tool]; + uint16_t tool = tools[idx_tool]; m_left_to_right = true; toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials. toolchange_Load(writer, cleaning_box); // Prime the tool. @@ -1371,7 +1371,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() } // Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box -void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume) +void WipeTower::plan_toolchange(float z_par, float layer_height_par, uint16_t old_tool, uint16_t new_tool, bool brim, float wipe_volume) { assert(m_plan.empty() || m_plan.back().z <= z_par + WT_EPSILON); // refuses to add a layer below the last one diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 18689eee0..1cfea50e5 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -25,14 +25,14 @@ public: struct Extrusion { - Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} + Extrusion(const Vec2f &pos, float width, uint16_t tool) : pos(pos), width(width), tool(tool) {} // End position of this extrusion. Vec2f pos; // Width of a squished extrusion, corrected for the roundings of the squished extrusions. // This is left zero if it is a travel move. float width; // Current extruder index. - unsigned int tool; + uint16_t tool; }; struct ToolChangeResult @@ -88,7 +88,7 @@ public: // Appends into internal structure m_plan containing info about the future wipe tower // to be used before building begins. The entries must be added ordered in z. - void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f); + void plan_toolchange(float z_par, float layer_height_par, uint16_t old_tool, uint16_t new_tool, bool brim, float wipe_volume = 0.f); // Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result" void generate(std::vector> &result); @@ -146,7 +146,7 @@ public: // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. - const std::vector &tools, + const std::vector &tools, // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower. // If false, the last priming are will be large enough to wipe the last extruder sufficiently. bool last_wipe_inside_wipe_tower); @@ -259,9 +259,9 @@ private: // State of the wipe tower generator. - unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics. - unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics. - ///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes. + uint32_t m_num_layer_changes = 0; // Layer change counter for the output statistics. + uint32_t m_num_tool_changes = 0; // Tool change change counter for the output statistics. + ///uint16_t m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes. bool m_print_brim = true; // A fill-in direction (positive Y, negative Y) alternates with each layer. wipe_shape m_current_shape = SHAPE_NORMAL; diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index b4aa9c172..d666556fa 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -26,18 +26,31 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) print_config.machine_max_acceleration_extruding.values.front() : 0); } -void GCodeWriter::set_extruders(std::vector extruder_ids) +void GCodeWriter::set_extruders(std::vector extruder_ids) { std::sort(extruder_ids.begin(), extruder_ids.end()); m_extruders.clear(); m_extruders.reserve(extruder_ids.size()); - for (unsigned int extruder_id : extruder_ids) + for (uint16_t extruder_id : extruder_ids) m_extruders.emplace_back(Extruder(extruder_id, &this->config)); - + /* we enable support for multiple extruder if any extruder greater than 0 is used (even if prints only uses that one) since we need to output Tx commands first extruder has index 0 */ - this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0; + this->multiple_extruders = this->multiple_extruders || (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0; +} + +void GCodeWriter::set_mills(std::vector mill_ids) +{ + std::sort(mill_ids.begin(), mill_ids.end()); + m_millers.clear(); + m_millers.reserve(mill_ids.size()); + for (uint16_t mill_id : mill_ids) { + m_millers.emplace_back(Mill(mill_id, &this->config)); + } + + /* we enable support for multiple extruder */ + this->multiple_extruders = this->multiple_extruders || !mill_ids.empty(); } std::string GCodeWriter::preamble() @@ -211,10 +224,10 @@ std::string GCodeWriter::reset_e(bool force) || FLAVOR_IS(gcfSailfish)) return ""; - if (m_extruder != nullptr) { - if (m_extruder->E() == 0. && ! force) + if (m_tool != nullptr) { + if (m_tool->E() == 0. && ! force) return ""; - m_extruder->reset_E(); + m_tool->reset_E(); } if (! m_extrusion_axis.empty() && ! this->config.use_relative_e_distances) { @@ -251,18 +264,35 @@ std::string GCodeWriter::toolchange_prefix() const "T"; } -std::string GCodeWriter::toolchange(unsigned int extruder_id) +std::string GCodeWriter::toolchange(unsigned int tool_id) { // set the new extruder - auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; }); - assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id); - m_extruder = &*it_extruder; + /*auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [tool_id](const Extruder &e) { return e.id() < tool_id; }); + assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id);*/ + //less optimized but it's easier to modify and it's not needed, as it's not called often. + bool found = false; + for (Extruder& extruder : m_extruders) { + if (tool_id == extruder.id()) { + m_tool = &extruder; + found = true; + break; + } + } + if (!found) { + for (Tool& mill : m_millers) { + if (tool_id == mill.id()) { + m_tool = &mill; + found = true; + break; + } + } + } // return the toolchange command // if we are running a single-extruder setup, just set the extruder and return nothing std::ostringstream gcode; if (this->multiple_extruders) { - gcode << this->toolchange_prefix() << extruder_id; + gcode << this->toolchange_prefix() << tool_id; if (this->config.gcode_comments) gcode << " ; change extruder"; gcode << "\n"; @@ -375,12 +405,13 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: { m_pos.x() = point.x(); m_pos.y() = point.y(); - m_extruder->extrude(dE); + bool is_extrude = m_tool->extrude(dE) != 0; std::ostringstream gcode; gcode << "G1 X" << XYZF_NUM(point.x()) - << " Y" << XYZF_NUM(point.y()) - << " " << m_extrusion_axis << E_NUM(m_extruder->E()); + << " Y" << XYZF_NUM(point.y()); + if(is_extrude) + gcode << " " << m_extrusion_axis << E_NUM(m_tool->E()); COMMENT(comment); gcode << "\n"; return gcode.str(); @@ -391,13 +422,14 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std m_pos.x() = point.x(); m_pos.y() = point.y(); m_lifted = 0; - m_extruder->extrude(dE); + bool is_extrude = m_tool->extrude(dE) != 0; std::ostringstream gcode; gcode << "G1 X" << XYZF_NUM(point.x()) - << " Y" << XYZF_NUM(point.y()) - << " Z" << XYZF_NUM(point.z() + m_pos.z()) - << " " << m_extrusion_axis << E_NUM(m_extruder->E()); + << " Y" << XYZF_NUM(point.y()) + << " Z" << XYZF_NUM(point.z() + m_pos.z()); + if (is_extrude) + gcode << " " << m_extrusion_axis << E_NUM(m_tool->E()); COMMENT(comment); gcode << "\n"; return gcode.str(); @@ -405,22 +437,22 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std std::string GCodeWriter::retract(bool before_wipe) { - double factor = before_wipe ? m_extruder->retract_before_wipe() : 1.; + double factor = before_wipe ? m_tool->retract_before_wipe() : 1.; assert(factor >= 0. && factor <= 1. + EPSILON); return this->_retract( - factor * m_extruder->retract_length(), - factor * m_extruder->retract_restart_extra(), + factor * m_tool->retract_length(), + factor * m_tool->retract_restart_extra(), "retract" ); } std::string GCodeWriter::retract_for_toolchange(bool before_wipe) { - double factor = before_wipe ? m_extruder->retract_before_wipe() : 1.; + double factor = before_wipe ? m_tool->retract_before_wipe() : 1.; assert(factor >= 0. && factor <= 1. + EPSILON); return this->_retract( - factor * m_extruder->retract_length_toolchange(), - factor * m_extruder->retract_restart_extra_toolchange(), + factor * m_tool->retract_length_toolchange(), + factor * m_tool->retract_restart_extra_toolchange(), "retract for toolchange" ); } @@ -436,13 +468,13 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std // If we use volumetric E values we turn lengths into volumes */ if (this->config.use_volumetric_e) { - double d = m_extruder->filament_diameter(); + double d = m_tool->filament_diameter(); double area = d * d * PI/4; length = length * area; restart_extra = restart_extra * area; } - double dE = m_extruder->retract(length, restart_extra); + double dE = m_tool->retract(length, restart_extra); if (dE != 0) { if (this->config.use_firmware_retraction) { if (FLAVOR_IS(gcfMachinekit)) @@ -450,8 +482,8 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std else gcode << "G10 ; retract\n"; } else { - gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E()) - << " F" << float(m_extruder->retract_speed() * 60.); + gcode << "G1 " << m_extrusion_axis << E_NUM(m_tool->E()) + << " F" << float(m_tool->retract_speed() * 60.); COMMENT(comment); gcode << "\n"; } @@ -470,7 +502,7 @@ std::string GCodeWriter::unretract() if (FLAVOR_IS(gcfMakerWare)) gcode << "M101 ; extruder on\n"; - double dE = m_extruder->unretract(); + double dE = m_tool->unretract(); if (dE != 0) { if (this->config.use_firmware_retraction) { if (FLAVOR_IS(gcfMachinekit)) @@ -480,8 +512,8 @@ std::string GCodeWriter::unretract() gcode << this->reset_e(); } else { // use G1 instead of G0 because G0 will blend the restart with the previous travel move - gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E()) - << " F" << float(m_extruder->deretract_speed() * 60.); + gcode << "G1 " << m_extrusion_axis << E_NUM(m_tool->E()) + << " F" << float(m_tool->deretract_speed() * 60.); if (this->config.gcode_comments) gcode << " ; unretract"; gcode << "\n"; } @@ -497,11 +529,14 @@ std::string GCodeWriter::lift() { // check whether the above/below conditions are met double target_lift = 0; - { - double above = this->config.retract_lift_above.get_at(m_extruder->id()); - double below = this->config.retract_lift_below.get_at(m_extruder->id()); + if(this->tool_is_extruder()){ + //these two should be in the Tool class methods.... + double above = this->config.retract_lift_above.get_at(m_tool->id()); + double below = this->config.retract_lift_below.get_at(m_tool->id()); if (m_pos.z() >= above && (below == 0 || m_pos.z() <= below)) - target_lift = this->config.retract_lift.get_at(m_extruder->id()); + target_lift = m_tool->retract_lift(); + } else { + target_lift = m_tool->retract_lift(); } if (this->extra_lift > 0) { diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 564cf8f9a..a76aae8b6 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -17,27 +17,49 @@ public: bool multiple_extruders; GCodeWriter() : - multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr), + multiple_extruders(false), m_extrusion_axis("E"), m_tool(nullptr), m_single_extruder_multi_material(false), m_last_acceleration(0), m_max_acceleration(0), m_last_fan_speed(0), m_last_bed_temperature(0), m_last_bed_temperature_reached(true), m_lifted(0) {} - Extruder* extruder() { return m_extruder; } - const Extruder* extruder() const { return m_extruder; } + Tool* tool() { return m_tool; } + const Tool* tool() const { return m_tool; } - std::string extrusion_axis() const { return m_extrusion_axis; } - void apply_print_config(const PrintConfig &print_config); + std::string extrusion_axis() const { return m_extrusion_axis; } + void apply_print_config(const PrintConfig &print_config); // Extruders are expected to be sorted in an increasing order. - void set_extruders(std::vector extruder_ids); + void set_extruders(std::vector extruder_ids); const std::vector& extruders() const { return m_extruders; } - std::vector extruder_ids() const { - std::vector out; - out.reserve(m_extruders.size()); - for (const Extruder &e : m_extruders) - out.push_back(e.id()); + std::vector extruder_ids() const { + std::vector out; + out.reserve(m_extruders.size()); + for (const Extruder& e : m_extruders) + out.push_back(e.id()); return out; } + void set_mills(std::vector extruder_ids); + const std::vector& mills() const { return m_millers; } + std::vector mill_ids() const { + std::vector out; + out.reserve(m_millers.size()); + for (const Tool& e : m_millers) + out.push_back(e.id()); + return out; + } + //give the first mill id or an id after the last extruder. Can be used to see if an id is an extruder or a mill + uint16_t first_mill() const { + if (m_millers.empty()) { + uint16_t max = 0; + for (const Extruder& e : m_extruders) + max = std::max(max, e.id()); + max++; + return (uint16_t)max; + }else return m_millers.front().id(); + } + bool tool_is_extruder() const { + return m_tool->id() < first_mill(); + } std::string preamble(); std::string postamble() const; std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const; @@ -47,14 +69,14 @@ public: std::string reset_e(bool force = false); std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const; // return false if this extruder was already selected - bool need_toolchange(unsigned int extruder_id) const - { return m_extruder == nullptr || m_extruder->id() != extruder_id; } - std::string set_extruder(unsigned int extruder_id) - { return this->need_toolchange(extruder_id) ? this->toolchange(extruder_id) : ""; } + bool need_toolchange(unsigned int tool_id) const + { return m_tool == nullptr || m_tool->id() != tool_id; } + std::string set_tool(unsigned int tool_id) + { return this->need_toolchange(tool_id) ? this->toolchange(tool_id) : ""; } // Prefix of the toolchange G-code line, to be used by the CoolingBuffer to separate sections of the G-code // printed with the same extruder. std::string toolchange_prefix() const; - std::string toolchange(unsigned int extruder_id); + std::string toolchange(unsigned int tool_id); std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const; std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()); std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string()); @@ -73,9 +95,10 @@ public: private: // Extruders are sorted by their ID, so that binary search is possible. std::vector m_extruders; + std::vector m_millers; std::string m_extrusion_axis; bool m_single_extruder_multi_material; - Extruder* m_extruder; + Tool* m_tool; unsigned int m_last_acceleration; // Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware. // If set to zero, the limit is not in action. diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 0729f99ae..b03357d63 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -220,6 +220,72 @@ void Layer::make_perimeters() BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done"; } +void Layer::make_milling_post_process() { + if (this->object()->print()->config().milling_diameter.empty()) return; + + BOOST_LOG_TRIVIAL(trace) << "Generating milling_post_process for layer " << this->id(); + + // keep track of regions whose perimeters we have already generated + std::vector done(m_regions.size(), false); + + for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++layerm) { + if ((*layerm)->slices().empty()) { + (*layerm)->milling.clear(); + } else { + size_t region_id = layerm - m_regions.begin(); + if (done[region_id]) + continue; + BOOST_LOG_TRIVIAL(trace) << "Generating milling_post_process for layer " << this->id() << ", region " << region_id; + done[region_id] = true; + const PrintRegionConfig& config = (*layerm)->region()->config(); + + // find compatible regions + LayerRegionPtrs layerms; + layerms.push_back(*layerm); + for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) { + LayerRegion* other_layerm = *it; + const PrintRegionConfig& other_config = other_layerm->region()->config(); + if (other_layerm->slices().empty()) continue; + /// !!! add here the settings you want to be added in the per-object menu. + /// if you don't do that, objects will share the same region, and the same settings. + if (config.milling_post_process == other_config.milling_post_process + && config.milling_extra_size == other_config.milling_extra_size + && (config.milling_after_z == other_config.milling_after_z || + this->bottom_z() > std::min(config.milling_after_z.get_abs_value(this->object()->print()->config().milling_diameter.values[0]), + other_config.milling_after_z.get_abs_value(this->object()->print()->config().milling_diameter.values[0])))) { + layerms.push_back(other_layerm); + done[it - m_regions.begin()] = true; + } + } + + if (layerms.size() == 1) { // optimization + (*layerm)->make_milling_post_process((*layerm)->slices()); + } else { + SurfaceCollection new_slices; + // Use a region if you ned a specific settgin, be sure to choose the good one, curtly there is o need. + LayerRegion* layerm_config = layerms.front(); + { + // group slices (surfaces) according to number of extra perimeters + std::map slices; // extra_perimeters => [ surface, surface... ] + for (LayerRegion* layerm : layerms) { + for (const Surface& surface : layerm->slices().surfaces) + slices[surface.extra_perimeters].emplace_back(surface); + layerm->milling.clear(); + } + // merge the surfaces assigned to each group + for (std::pair& surfaces_with_extra_perimeters : slices) + new_slices.append(union_ex(surfaces_with_extra_perimeters.second, true), surfaces_with_extra_perimeters.second.front()); + } + + // make perimeters + layerm_config->make_milling_post_process(new_slices); + + } + } + } + BOOST_LOG_TRIVIAL(trace) << "Generating milling_post_process for layer " << this->id() << " - Done"; +} + void Layer::make_fills() { #ifdef SLIC3R_DEBUG diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 15ae44d7b..d567518a3 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -55,6 +55,9 @@ public: // (this collection contains only ExtrusionEntityCollection objects) ExtrusionEntityCollection perimeters; + // collection of expolygons representing the milling path of the first milling cutter + ExtrusionEntityCollection milling; + // ordered collection of extrusion paths to fill surfaces // (this collection contains only ExtrusionEntityCollection objects) ExtrusionEntityCollection fills; @@ -62,7 +65,8 @@ public: Flow flow(FlowRole role, bool bridge = false, double width = -1) const; void slices_to_fill_surfaces_clipped(); void prepare_fill_surfaces(); - void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); + void make_perimeters(const SurfaceCollection& slices, SurfaceCollection* fill_surfaces); + void make_milling_post_process(const SurfaceCollection& slices); void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered); double infill_area_threshold() const; // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer. @@ -142,6 +146,7 @@ public: return false; } void make_perimeters(); + void make_milling_post_process(); void make_fills(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index d72c0dabb..548e0aa7d 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -2,6 +2,7 @@ #include "BridgeDetector.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" +#include "Milling/MillingPostProcess.hpp" #include "PerimeterGenerator.hpp" #include "Print.hpp" #include "Surface.hpp" @@ -61,7 +62,6 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec PerimeterGenerator g( // input: &slices, - this->layer()->height, this->flow(frPerimeter), &this->region()->config(), &this->layer()->object()->config(), @@ -79,7 +79,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec if (this->layer()->upper_layer != NULL) g.upper_slices = &this->layer()->upper_layer->lslices; - g.layer_id = (int)this->layer()->id(); + g.layer = this->layer(); g.ext_perimeter_flow = this->flow(frExternalPerimeter); g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object()); g.solid_infill_flow = this->flow(frSolidInfill); @@ -89,6 +89,17 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec this->fill_no_overlap_expolygons = g.fill_no_overlap; } +void LayerRegion::make_milling_post_process(const SurfaceCollection& slices) { + MillingPostProcess mill(// input: + &slices, + (this->layer()->lower_layer != nullptr) ? &this->layer()->lower_layer->lslices : nullptr, + &this->region()->config(), + &this->layer()->object()->config(), + &this->layer()->object()->print()->config() + ); + milling = mill.process(this->layer()); +} + //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 #define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. diff --git a/src/libslic3r/Milling/MillingPostProcess.cpp b/src/libslic3r/Milling/MillingPostProcess.cpp new file mode 100644 index 000000000..0b45bb239 --- /dev/null +++ b/src/libslic3r/Milling/MillingPostProcess.cpp @@ -0,0 +1,169 @@ +#include "MillingPostProcess.hpp" +#include "../Layer.hpp" + +namespace Slic3r { + + void MillingPostProcess::getExtrusionLoop(const Layer* layer, Polygon& poly, Polylines& entrypoints, ExtrusionEntityCollection& out_coll) { + + + const double milling_diameter = scale_(this->print_config->milling_diameter.get_at(0)); + + //get the longest polyline + Polyline best_polyline; + for (Polyline& polyline : entrypoints) + if (polyline.size() > best_polyline.size()) + best_polyline = polyline; + + if (!entrypoints.empty() && best_polyline.size() > 3 ) { + + + //get two pair of points that are at less than max_dist. + int32_t first_point_extract_idx = 1; + int32_t first_point_idx = -1; + const double dist_max_square = milling_diameter * milling_diameter / 4; + double best_dist = dist_max_square; + for (int32_t idx = 0; idx < poly.points.size(); idx++) { + if (poly.points[idx].distance_to_square(best_polyline.points[first_point_extract_idx]) < best_dist) { + best_dist = poly.points[idx].distance_to_square(best_polyline.points[first_point_extract_idx]); + first_point_idx = idx; + } + } + if (first_point_idx > -1) { + //now search the second one + int32_t second_point_idx = -1; + for (int32_t idx = first_point_idx +1; idx < poly.points.size(); idx++) { + if (poly.points[idx].distance_to_square(poly.points[first_point_idx]) > dist_max_square) { + second_point_idx = idx; + break; + } + } + if(second_point_idx == -1) + for (int32_t idx = 0; idx < first_point_idx; idx++) { + if (poly.points[idx].distance_to_square(poly.points[first_point_idx]) > dist_max_square) { + second_point_idx = idx; + break; + } + } + + int32_t second_point_extract_idx = first_point_extract_idx; + if (second_point_idx == -1) { + second_point_idx = first_point_idx; + } else { + //now see if an other extract point is nearer + best_dist = poly.points[second_point_idx].distance_to_square(best_polyline.points[second_point_extract_idx]); + for (int32_t idx = 0; idx < best_polyline.points.size(); idx++) { + if (poly.points[second_point_idx].distance_to_square(best_polyline.points[idx]) < best_dist) { + best_dist = poly.points[second_point_idx].distance_to_square(best_polyline.points[idx]); + second_point_extract_idx = idx; + } + } + } + + ExtrusionPath contour(erMilling); + contour.mm3_per_mm = 0; + contour.width = (float)this->print_config->milling_diameter.get_at(0); + contour.height = (float)layer->height; + contour.polyline.points.push_back(best_polyline.points[first_point_extract_idx]); + for (int32_t idx = first_point_idx; idx < poly.points.size(); idx++) { + contour.polyline.points.push_back(poly.points[idx]); + } + if (second_point_idx <= first_point_idx) { + for (int32_t idx = 0; idx < poly.points.size(); idx++) { + contour.polyline.points.push_back(poly.points[idx]); + } + } + for (int32_t idx = 0; idx < second_point_idx + 1; idx++) { + contour.polyline.points.push_back(poly.points[idx]); + } + contour.polyline.points.push_back(best_polyline.points[second_point_extract_idx]); + + out_coll.append(std::move(contour)); + return; + + + } + } + //default path, without safe-guard up-down. + ExtrusionPath contour(erMilling); + contour.polyline = poly.split_at_first_point(); + if (contour.polyline.points.size() > 3) + contour.polyline.points.push_back(contour.polyline.points[1]); + contour.mm3_per_mm = 0; + contour.width = this->print_config->milling_diameter.get_at(0); + contour.height = (float)layer->height; + out_coll.append(std::move(contour)); + return; + + } + + ExtrusionEntityCollection MillingPostProcess::process(const Layer* layer) + { + if (!can_be_milled(layer)) return ExtrusionEntityCollection(); + + const double milling_diameter = scale_(this->print_config->milling_diameter.get_at(0)); + + ExPolygons milling_lines; + for (const Surface& surf : slices->surfaces) { + ExPolygons surf_milling = offset_ex(surf.expolygon, milling_diameter/2, ClipperLib::jtRound); + for (const ExPolygon& expoly : surf_milling) + expoly.simplify(SCALED_RESOLUTION, &milling_lines); + } + milling_lines = union_ex(milling_lines); + + ExPolygons secured_points = offset_ex(milling_lines, milling_diameter / 3); + ExPolygons entrypoints; + for (const ExPolygon& expoly : secured_points) + expoly.simplify(SCALED_RESOLUTION, &entrypoints); + entrypoints = union_ex(entrypoints); + Polygons entrypoints_poly; + for (const ExPolygon& expoly : secured_points) + entrypoints_poly.emplace_back(expoly); + + ExtrusionEntityCollection all_milling; + for (ExPolygon &ex_poly : milling_lines) { + getExtrusionLoop(layer, ex_poly.contour, intersection_pl(offset(ex_poly.contour, milling_diameter / 4), entrypoints_poly), all_milling); + for (Polygon& hole : ex_poly.holes) { + getExtrusionLoop(layer, hole, intersection_pl(offset(hole, milling_diameter / 3), entrypoints_poly), all_milling); + } + } + + return all_milling; + } + + bool MillingPostProcess::can_be_milled(const Layer* layer) { + return !print_config->milling_diameter.values.empty() && config->milling_post_process + && layer->bottom_z() >= config->milling_after_z.get_abs_value(this->object_config->first_layer_height.get_abs_value(this->print_config->nozzle_diameter.values.front())); + } + + ExPolygons MillingPostProcess::get_unmillable_areas(const Layer* layer) + { + if (!can_be_milled(layer)) return ExPolygons(); + + const coord_t milling_radius = scale_(this->print_config->milling_diameter.get_at(0)) / 2; + + ExPolygons milling_lines; + ExPolygons surfaces; + for (const Surface& surf : slices->surfaces) { + ExPolygons surf_milling = offset_ex(surf.expolygon, milling_radius, ClipperLib::jtRound); + for (const ExPolygon& expoly : surf_milling) + expoly.simplify(SCALED_RESOLUTION, &milling_lines); + surfaces.push_back(surf.expolygon); + } + union_ex(milling_lines, true); + union_ex(surfaces, true); + + + ExPolygons exact_unmillable_area = diff_ex(offset_ex(milling_lines, -milling_radius, ClipperLib::jtRound), surfaces, true); + if (exact_unmillable_area.empty()) + return exact_unmillable_area; + + //increae a bit the computed unmillable_area to be sure the mill will mill all the plastic + coord_t safety_offset = milling_radius / 2; + ExPolygons safe_umillable = diff_ex(offset_ex(exact_unmillable_area, safety_offset), surfaces, true); + ExPolygons safe_umillable_simplified; + for (const ExPolygon& expoly : safe_umillable) + expoly.simplify(SCALED_RESOLUTION, &safe_umillable_simplified); + return union_ex(safe_umillable_simplified, true); + } + +} diff --git a/src/libslic3r/Milling/MillingPostProcess.hpp b/src/libslic3r/Milling/MillingPostProcess.hpp new file mode 100644 index 000000000..5fd168775 --- /dev/null +++ b/src/libslic3r/Milling/MillingPostProcess.hpp @@ -0,0 +1,49 @@ +#ifndef slic3r_MillingPostProcess_hpp_ +#define slic3r_MillingPostProcess_hpp_ + +#include "../libslic3r.h" +#include +#include "../ExPolygonCollection.hpp" +#include "../Polygon.hpp" +#include "../Layer.hpp" +#include "../PrintConfig.hpp" +#include "../SurfaceCollection.hpp" + +namespace Slic3r { + + +class MillingPostProcess { +public: + // Inputs: + const SurfaceCollection *slices; + const ExPolygons *lower_slices; + const PrintRegionConfig *config; + const PrintObjectConfig *object_config; + const PrintConfig *print_config; + + MillingPostProcess( + // Input: + const SurfaceCollection* slices, + const ExPolygons* lower_slices, + const PrintRegionConfig* config, + const PrintObjectConfig* object_config, + const PrintConfig* print_config) + : slices(slices), lower_slices(lower_slices), + config(config), object_config(object_config), print_config(print_config) + {}; + + /// get mill path + ExtrusionEntityCollection process(const Layer *layer); + /// get the areas that shouldn't be grown because the mill can't go here. + ExPolygons get_unmillable_areas(const Layer* layer); + bool can_be_milled(const Layer* layer); + +private: + Polygons _lower_slices_p; + + void getExtrusionLoop(const Layer* layer, Polygon& poly, Polylines& entrypoints, ExtrusionEntityCollection&out_coll); +}; + +} + +#endif diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index ecdd90e38..2b8287e76 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -12,6 +12,7 @@ #include "BoundingBox.hpp" #include "ExPolygon.hpp" #include "Geometry.hpp" +#include "Milling/MillingPostProcess.hpp" #include "Polygon.hpp" #include "Line.hpp" #include "ClipperUtils.hpp" @@ -86,6 +87,19 @@ void PerimeterGenerator::process() // in the current layer this->_lower_slices_p = offset(*this->lower_slices, double(scale_(config->overhangs_width.get_abs_value(nozzle_diameter)))); } + + // have to grown the periemters if mill post-process + MillingPostProcess miller(this->slices, this->lower_slices, config, object_config, print_config); + bool have_to_grow_for_miller = miller.can_be_milled(layer) && config->milling_extra_size.get_abs_value(1) > 0; + ExPolygons unmillable; + coord_t mill_extra_size = 0; + if (have_to_grow_for_miller) { + unmillable = miller.get_unmillable_areas(layer); + double spacing_vs_width = ext_perimeter_flow.width - ext_perimeter_flow.spacing(); + mill_extra_size = scale_(config->milling_extra_size.get_abs_value(spacing_vs_width)); + have_to_grow_for_miller = mill_extra_size > SCALED_EPSILON; + } + // we need to process each island separately because we might have different // extra perimeters for each one @@ -287,7 +301,7 @@ void PerimeterGenerator::process() } surface_idx = 0; - const int extra_odd_perimeter = (config->extra_perimeters_odd_layers && layer_id % 2 == 1 ? 1:0); + const int extra_odd_perimeter = (config->extra_perimeters_odd_layers && layer->id() % 2 == 1 ? 1:0); for (const Surface &surface : all_surfaces) { // detect how many perimeters must be generated for this island int loop_number = this->config->perimeters + surface.extra_perimeters - 1 + extra_odd_perimeter; // 0-indexed loops @@ -303,9 +317,21 @@ void PerimeterGenerator::process() ExPolygons last = union_ex(surface.expolygon.simplify_p(SCALED_RESOLUTION)); if (loop_number >= 0) { + //increase surface for milling_post-process + if (have_to_grow_for_miller) { + if (unmillable.empty()) + last = offset_ex(last, mill_extra_size); + else { + ExPolygons growth = diff_ex(offset_ex(last, mill_extra_size), unmillable, true); + last.insert(last.end(), growth.begin(), growth.end()); + last = union_ex(last); + } + } + + // Add perimeters on overhangs : initialization ExPolygons overhangs_unsupported; - if ( (this->config->extra_perimeters_overhangs || (this->config->overhangs_reverse && this->layer_id % 2 == 1)) + if ( (this->config->extra_perimeters_overhangs || (this->config->overhangs_reverse && this->layer->id() % 2 == 1)) && !last.empty() && this->lower_slices != NULL && !this->lower_slices->empty()) { //remove holes from lower layer, we only ant that for overhangs, not bridges! ExPolygons lower_without_holes; @@ -341,11 +367,11 @@ void PerimeterGenerator::process() } } bool has_steep_overhang = false; - if (this->layer_id % 2 == 1 && this->config->overhangs_reverse //check if my option is set and good layer + if (this->layer->id() % 2 == 1 && this->config->overhangs_reverse //check if my option is set and good layer && !last.empty() && !overhangs_unsupported.empty() //has something to work with ) { coord_t offset = scale_(config->overhangs_reverse_threshold.get_abs_value(this->perimeter_flow.width)); - //version with °: scale_(std::tan(PI * (0.5f / 90) * config->overhangs_reverse_threshold.value ) * this->layer_height) + //version with °: scale_(std::tan(PI * (0.5f / 90) * config->overhangs_reverse_threshold.value ) * this->layer->height) if (offset_ex(overhangs_unsupported, -offset / 2).size() > 0) { //allow this loop to be printed in reverse @@ -444,7 +470,7 @@ void PerimeterGenerator::process() bound.remove_point_too_near((coord_t)SCALED_RESOLUTION); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop (*1.2 because of circles approx. and enlrgment from 'div') Slic3r::MedialAxis ma{ thin[0], (coord_t)((ext_perimeter_width + ext_perimeter_spacing)*1.2), - min_width, coord_t(this->layer_height) }; + min_width, coord_t(this->layer->height) }; ma.use_bounds(bound) .use_min_real_width((coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter)) .use_tapers(overlap) @@ -554,7 +580,12 @@ void PerimeterGenerator::process() offset_top_surface -= 0.9 * (config->perimeters <= 1 ? 0. : (perimeter_spacing * (config->perimeters - 1))); else offset_top_surface = 0; // get the real top surface - ExPolygons top_polygons = diff_ex(last, *this->upper_slices, true); + ExPolygons top_polygons = (!have_to_grow_for_miller) ? diff_ex(last, *this->upper_slices, true) + :(unmillable.empty())? + diff_ex(last, offset_ex(*this->upper_slices, mill_extra_size), true) + : + diff_ex(last, diff_ex(offset_ex(*this->upper_slices, mill_extra_size), unmillable, true)); + //get the not-top surface, from the "real top" but enlarged by external_infill_margin ExPolygons inner_polygons = diff_ex(last, offset_ex(top_polygons, offset_top_surface), true); // get the enlarged top surface, by using inner_polygons instead of upper_slices @@ -651,7 +682,7 @@ void PerimeterGenerator::process() // we continue inwards after having finished the brim // TODO: add test for perimeter order if (this->config->external_perimeters_first || - (this->layer_id == 0 && this->object_config->brim_width.value > 0)) { + (this->layer->id() == 0 && this->object_config->brim_width.value > 0)) { if (this->config->external_perimeters_nothole.value) { if (this->config->external_perimeters_hole.value) { entities.reverse(); @@ -699,7 +730,7 @@ void PerimeterGenerator::process() // collapse double min = 0.2 * perimeter_width * (1 - INSET_OVERLAP_TOLERANCE); //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). - min = std::max(min, double(Flow::new_from_spacing(EPSILON, nozzle_diameter, this->layer_height, false).scaled_width())); + min = std::max(min, double(Flow::new_from_spacing(EPSILON, nozzle_diameter, this->layer->height, false).scaled_width())); double max = 2.2 * perimeter_spacing; //remove areas that are too big (shouldn't occur...) ExPolygons gaps_ex_to_test = diff_ex( @@ -762,7 +793,7 @@ void PerimeterGenerator::process() // create lines from the area ThickPolylines polylines; for (const ExPolygon &ex : gaps_ex) { - MedialAxis{ ex, coord_t(max*1.1), coord_t(min), coord_t(this->layer_height) }.build(polylines); + MedialAxis{ ex, coord_t(max*1.1), coord_t(min), coord_t(this->layer->height) }.build(polylines); } // create extrusion from lines if (!polylines.empty()) { @@ -852,7 +883,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( // detect overhanging/bridging perimeters ExtrusionPaths paths; - if (this->config->overhangs && this->layer_id > 0 + if (this->config->overhangs && this->layer->id() > 0 && !(this->object_config->support_material && this->object_config->support_material_contact_distance_type.value == zdNone)) { // get non-overhang paths by intersecting this loop with the grown lower slices extrusion_paths_append( @@ -861,7 +892,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( role, is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm, is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width, - (float) this->layer_height); + (float) this->layer->height); // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between @@ -882,7 +913,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( path.polyline = loop.polygon.split_at_first_point(); path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm; path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width; - path.height = (float) this->layer_height; + path.height = (float) this->layer->height; paths.push_back(path); } @@ -923,8 +954,8 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( ExtrusionLoop *eloop = static_cast(coll.entities[idx.first]); coll.entities[idx.first] = nullptr; if (loop.is_contour) { - //note: this->layer_id % 2 == 1 already taken into account in the is_steep_overhang compute (to save time). - if (loop.is_steep_overhang && this->layer_id % 2 == 1) + //note: this->layer->id() % 2 == 1 already taken into account in the is_steep_overhang compute (to save time). + if (loop.is_steep_overhang && this->layer->id() % 2 == 1) eloop->make_clockwise(); else eloop->make_counter_clockwise(); @@ -1071,7 +1102,7 @@ PerimeterGenerator::_extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, co loop.is_external() ? erExternalPerimeter : erPerimeter, (double)(loop.is_external() ? this->_ext_mm3_per_mm : this->_mm3_per_mm), (float)(loop.is_external() ? this->ext_perimeter_flow.width : this->perimeter_flow.width), - (float)(this->layer_height)); + (float)(this->layer->height)); single_point.paths.back().polyline = poly_point; return single_point; } @@ -1119,7 +1150,7 @@ PerimeterGenerator::_extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, co } // detect overhanging/bridging perimeters - if (this->config->overhangs && this->layer_id > 0 + if (this->config->overhangs && this->layer->id() > 0 && !(this->object_config->support_material && this->object_config->support_material_contact_distance_type.value == zdNone)) { ExtrusionPaths paths; // get non-overhang paths by intersecting this loop with the grown lower slices @@ -1129,7 +1160,7 @@ PerimeterGenerator::_extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, co role, is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm, is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width, - (float) this->layer_height); + (float) this->layer->height); // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between @@ -1203,7 +1234,7 @@ PerimeterGenerator::_extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, co if (need_to_reverse) path.polyline.reverse(); path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm; path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width; - path.height = (float)(this->layer_height); + path.height = (float)(this->layer->height); my_loop.paths.emplace_back(path); } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 070b03e50..8d66520b8 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -5,6 +5,7 @@ #include #include "ExPolygonCollection.hpp" #include "Flow.hpp" +#include "Layer.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" #include "SurfaceCollection.hpp" @@ -54,8 +55,7 @@ public: const SurfaceCollection *slices; const ExPolygons *upper_slices; const ExPolygons *lower_slices; - double layer_height; - int layer_id; + Layer *layer; Flow perimeter_flow; Flow ext_perimeter_flow; Flow overhang_flow; @@ -71,8 +71,7 @@ public: PerimeterGenerator( // Input: - const SurfaceCollection* slices, - double layer_height, + const SurfaceCollection* slices, Flow flow, const PrintRegionConfig* config, const PrintObjectConfig* object_config, @@ -84,8 +83,8 @@ public: ExtrusionEntityCollection* gap_fill, // Infills without the gap fills SurfaceCollection* fill_surfaces) - : slices(slices), lower_slices(nullptr), upper_slices(nullptr), layer_height(layer_height), - layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), + : slices(slices), lower_slices(nullptr), upper_slices(nullptr), + perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), solid_infill_flow(flow), config(config), object_config(object_config), print_config(print_config), loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0da8e6fdb..4d386bd4c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -117,6 +117,11 @@ bool Print::invalidate_state_by_config_options(const std::vector Print::object_extruders(const PrintObjectPtrs &objects) const +std::vector Print::object_extruders(const PrintObjectPtrs &objects) const { - std::vector extruders; + std::vector extruders; extruders.reserve(m_regions.size() * 3); std::vector region_used(m_regions.size(), false); for (const PrintObject *object : objects) @@ -329,11 +334,11 @@ std::vector Print::object_extruders(const PrintObjectPtrs &objects } // returns 0-based indices of used extruders -std::vector Print::support_material_extruders() const +std::vector Print::support_material_extruders() const { - std::vector extruders; + std::vector extruders; bool support_uses_current_extruder = false; - auto num_extruders = (unsigned int)m_config.nozzle_diameter.size(); + auto num_extruders = (uint16_t)m_config.nozzle_diameter.size(); for (PrintObject *object : m_objects) { if (object->has_support_material()) { @@ -341,14 +346,14 @@ std::vector Print::support_material_extruders() const if (object->config().support_material_extruder == 0) support_uses_current_extruder = true; else { - unsigned int i = (unsigned int)object->config().support_material_extruder - 1; + uint16_t i = (uint16_t)object->config().support_material_extruder - 1; extruders.emplace_back((i >= num_extruders) ? 0 : i); } assert(object->config().support_material_interface_extruder >= 0); if (object->config().support_material_interface_extruder == 0) support_uses_current_extruder = true; else { - unsigned int i = (unsigned int)object->config().support_material_interface_extruder - 1; + uint16_t i = (uint16_t)object->config().support_material_interface_extruder - 1; extruders.emplace_back((i >= num_extruders) ? 0 : i); } } @@ -363,19 +368,19 @@ std::vector Print::support_material_extruders() const } // returns 0-based indices of used extruders -std::vector Print::extruders() const +std::vector Print::extruders() const { - std::vector extruders = this->object_extruders(m_objects); + std::vector extruders = this->object_extruders(m_objects); append(extruders, this->support_material_extruders()); sort_remove_duplicates(extruders); return extruders; } -unsigned int Print::num_object_instances() const +uint16_t Print::num_object_instances() const { - size_t instances = 0; + uint16_t instances = 0; for (const PrintObject *print_object : m_objects) - instances += (unsigned int)print_object->instances().size(); + instances += (uint16_t)print_object->instances().size(); return instances; } @@ -1403,12 +1408,12 @@ std::string Print::validate() const } { - std::vector extruders = this->extruders(); + std::vector extruders = this->extruders(); // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits::max(); double max_nozzle_diameter = 0; - for (unsigned int extruder_id : extruders) { + for (uint16_t extruder_id : extruders) { double dmr = m_config.nozzle_diameter.get_at(extruder_id); min_nozzle_diameter = std::min(min_nozzle_diameter, dmr); max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); @@ -1724,7 +1729,7 @@ void Print::process() if (config().complete_objects) { for (PrintObject *obj : obj_group) { //get flow - std::vector set_extruders = this->object_extruders({ obj }); + std::vector set_extruders = this->object_extruders({ obj }); append(set_extruders, this->support_material_extruders()); sort_remove_duplicates(set_extruders); Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front()); @@ -1749,7 +1754,7 @@ void Print::process() if (obj_groups.size() > 1) brim_area = union_ex(brim_area); //get the first extruder in the list for these objects... replicating gcode generation - std::vector set_extruders = this->object_extruders(m_objects); + std::vector set_extruders = this->object_extruders(m_objects); append(set_extruders, this->support_material_extruders()); sort_remove_duplicates(set_extruders); Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front()); @@ -1885,7 +1890,7 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio std::vector extruders; std::vector extruders_e_per_mm; { - std::vector set_extruders = this->object_extruders(objects); + std::vector set_extruders = this->object_extruders(objects); append(set_extruders, this->support_material_extruders()); sort_remove_duplicates(set_extruders); extruders.reserve(set_extruders.size()); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index fdef40759..dda947714 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -57,7 +57,7 @@ public: const Print* print() const { return m_print; } const PrintRegionConfig& config() const { return m_config; } // 1-based extruder identifier for this region and role. - unsigned int extruder(FlowRole role) const; + uint16_t extruder(FlowRole role) const; Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; // Average diameter of nozzles participating on extruding this region. coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; @@ -65,8 +65,8 @@ public: coordf_t bridging_height_avg(const PrintConfig &print_config) const; // Collect 0-based extruder indices used to print this region's object. - void collect_object_printing_extruders(std::vector &object_extruders) const; - static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders); + void collect_object_printing_extruders(std::vector &object_extruders) const; + static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders); // Methods modifying the PrintRegion's state: public: @@ -184,7 +184,7 @@ public: static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z); // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) - std::vector object_extruders() const; + std::vector object_extruders() const; // Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t void slice(); @@ -404,9 +404,9 @@ public: Flow brim_flow(size_t extruder_id) const; Flow skirt_flow(size_t extruder_id) const; - std::vector object_extruders(const PrintObjectPtrs &objects) const; - std::vector support_material_extruders() const; - std::vector extruders() const; + std::vector object_extruders(const PrintObjectPtrs &objects) const; + std::vector support_material_extruders() const; + std::vector extruders() const; double max_allowed_layer_height() const; bool has_support_material() const; // Make sure the background processing has no access to this model_object during this call! @@ -422,7 +422,7 @@ public: const PrintRegionPtrs& regions() const { return m_regions; } // How many of PrintObject::copies() over all print objects are there? // If zero, then the print is empty and the print shall not be executed. - unsigned int num_object_instances() const; + uint16_t num_object_instances() const; const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& brim() const { return m_brim; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 9823006ad..a48c140d1 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -34,6 +34,8 @@ PrintConfigDef::PrintConfigDef() assign_printer_technology_to_unknown(this->options, ptFFF); this->init_sla_params(); assign_printer_technology_to_unknown(this->options, ptSLA); + this->init_milling_params(); + assign_printer_technology_to_unknown(this->options, ptMill); } void PrintConfigDef::init_common_params() @@ -1955,31 +1957,6 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(0)); #endif /* HAS_PRESSURE_EQUALIZER */ - def = this->add("milling_cutter", coInt); - def->gui_type = "i_enum_open"; - def->label = L("Milling cutter"); - def->category = OptionCategory::extruders; - def->tooltip = L("The milling cutter to use (unless more specific extruder settings are specified). "); - def->min = 0; // 0 = inherit defaults - def->enum_labels.push_back("default"); // override label for item 0 - def->enum_labels.push_back("1"); - def->enum_labels.push_back("2"); - def->enum_labels.push_back("3"); - def->enum_labels.push_back("4"); - def->enum_labels.push_back("5"); - def->enum_labels.push_back("6"); - def->enum_labels.push_back("7"); - def->enum_labels.push_back("8"); - def->enum_labels.push_back("9"); - - def = this->add("milling_diameter", coFloats); - def->label = L("Milling diameter"); - def->category = OptionCategory::extruders; - def->tooltip = L("This is the diameter of your cutting tool."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats(2.14)); - def = this->add("min_fan_speed", coInts); def->label = L("Min"); def->full_label = ("Min fan speed"); @@ -3497,9 +3474,129 @@ void PrintConfigDef::init_extruder_option_keys() "wipe_extra_perimeter" }; assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end())); +} + +void PrintConfigDef::init_milling_params() +{ m_milling_option_keys = { "milling_diameter", + "milling_toolchange_end_gcode", + "milling_toolchange_start_gcode", + //"milling_offset", + //"milling_z_offset", + "milling_z_lift", + }; + + ConfigOptionDef* def; + + // Milling Printer settings + + def = this->add("milling_cutter", coInt); + def->gui_type = "i_enum_open"; + def->label = L("Milling cutter"); + def->category = OptionCategory::general; + def->tooltip = L("The milling cutter to use (unless more specific extruder settings are specified). "); + def->min = 0; // 0 = inherit defaults + def->enum_labels.push_back("default"); // override label for item 0 + def->enum_labels.push_back("1"); + def->enum_labels.push_back("2"); + def->enum_labels.push_back("3"); + def->enum_labels.push_back("4"); + def->enum_labels.push_back("5"); + def->enum_labels.push_back("6"); + def->enum_labels.push_back("7"); + def->enum_labels.push_back("8"); + def->enum_labels.push_back("9"); + + def = this->add("milling_diameter", coFloats); + def->label = L("Milling diameter"); + def->category = OptionCategory::milling_extruders; + def->tooltip = L("This is the diameter of your cutting tool."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats(3.14)); + + def = this->add("milling_offset", coPoints); + def->label = L("Tool offset"); + def->category = OptionCategory::extruders; + def->tooltip = L("If your firmware doesn't handle the extruder displacement you need the G-code " + "to take it into account. This option lets you specify the displacement of each extruder " + "with respect to the first one. It expects positive coordinates (they will be subtracted " + "from the XY coordinate)."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoints( Vec2d(0,0) )); + + def = this->add("milling_z_offset", coFloats); + def->label = L("Tool z offset"); + def->category = OptionCategory::extruders; + def->tooltip = L("."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats(0)); + + def = this->add("milling_z_lift", coFloats); + def->label = L("Tool z lift"); + def->category = OptionCategory::extruders; + def->tooltip = L("Amount of lift for travel."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats(2)); + + def = this->add("milling_toolchange_start_gcode", coStrings); + def->label = L("G-Code to switch to this toolhead"); + def->category = OptionCategory::milling_extruders; + def->tooltip = L("Put here the gcode to change the toolhead (called after the g-code T[next_extruder]). You have access to [next_extruder] and [previous_extruder]." + " next_extruder is the 'extruder number' of the new milling tool, it's equal to the index (begining at 0) of the milling tool plus the number of extruders." + " previous_extruder is the 'extruder number' of the previous tool, it may be a normal extruder, if it's below the number of extruders." + " The numbe rof extruder is available at [extruder]and the number of milling tool is available at [milling_cutter]."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionStrings("")); + + def = this->add("milling_toolchange_end_gcode", coStrings); + def->label = L("G-Code to switch from this toolhead"); + def->category = OptionCategory::milling_extruders; + def->tooltip = L("Put here the gcode to end the toolhead action, like stopping the spindle. You have access to [next_extruder] and [previous_extruder]." + " previous_extruder is the 'extruder number' of the current milling tool, it's equal to the index (begining at 0) of the milling tool plus the number of extruders." + " next_extruder is the 'extruder number' of the next tool, it may be a normal extruder, if it's below the number of extruders." + " The numbe rof extruder is available at [extruder]and the number of milling tool is available at [milling_cutter]."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionStrings("")); + + def = this->add("milling_post_process", coBool); + def->label = L("Milling post-processing"); + def->category = OptionCategory::milling; + def->tooltip = L("If activated, at the end of each layer, the printer will switch to a milling ead and mill the external periemters." + "\nYou should set the 'Milling extra XY size' to a value high enough to have enough plastic to mill. Also, be sure that your piece is firmly glued to the bed."); + def->mode = comSimple; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("milling_extra_size", coFloatOrPercent); + def->label = L("Milling extra XY size"); + def->category = OptionCategory::milling; + def->tooltip = L("This increase the size of the object by a certain amount to have enough plastic to mill." + " You can set a number of mm or a percentage of the calculated optimal extra width (from flow calculation)."); + def->sidetext = L("mm or %"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(150, true)); + + def = this->add("milling_after_z", coFloatOrPercent); + def->label = L("Milling only after"); + def->category = OptionCategory::milling; + def->tooltip = L("THis setting restrict the post-process milling to a certain height, to avoid milling the bed. It can be a mm of a % of the first layer height (so it can depends of the object)."); + def->sidetext = L("mm or %"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(200, true)); + + def = this->add("milling_speed", coFloat); + def->label = L("Milling Speed"); + def->category = OptionCategory::milling; + def->tooltip = L("Speed for milling tool."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(30)); } void PrintConfigDef::init_sla_params() diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b12545090..efc3bc653 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -282,6 +282,7 @@ private: void init_fff_params(); void init_extruder_option_keys(); void init_sla_params(); + void init_milling_params(); std::vector m_extruder_option_keys; std::vector m_extruder_retract_keys; @@ -642,6 +643,10 @@ public: ConfigOptionBool infill_dense; ConfigOptionEnum infill_dense_algo; ConfigOptionBool infill_first; + ConfigOptionFloatOrPercent milling_after_z; + ConfigOptionFloatOrPercent milling_extra_size; + ConfigOptionBool milling_post_process; + ConfigOptionFloat milling_speed; // Detect bridging perimeters ConfigOptionBool overhangs; ConfigOptionFloatOrPercent overhangs_width; @@ -725,6 +730,10 @@ protected: OPT_PTR(infill_not_connected); OPT_PTR(infill_dense_algo); OPT_PTR(infill_first); + OPT_PTR(milling_after_z); + OPT_PTR(milling_extra_size); + OPT_PTR(milling_post_process); + OPT_PTR(milling_speed); OPT_PTR(overhangs); OPT_PTR(overhangs_width); OPT_PTR(overhangs_reverse); @@ -859,6 +868,7 @@ public: ConfigOptionString feature_gcode; ConfigOptionFloat max_print_speed; ConfigOptionFloat max_volumetric_speed; + ConfigOptionFloats milling_z_lift; #ifdef HAS_PRESSURE_EQUALIZER ConfigOptionFloat max_volumetric_extrusion_rate_slope_positive; ConfigOptionFloat max_volumetric_extrusion_rate_slope_negative; @@ -953,6 +963,7 @@ protected: OPT_PTR(feature_gcode); OPT_PTR(max_print_speed); OPT_PTR(max_volumetric_speed); + OPT_PTR(milling_z_lift); #ifdef HAS_PRESSURE_EQUALIZER OPT_PTR(max_volumetric_extrusion_rate_slope_positive); OPT_PTR(max_volumetric_extrusion_rate_slope_negative); @@ -1038,6 +1049,10 @@ public: ConfigOptionFloats max_layer_height; ConfigOptionFloat max_print_height; ConfigOptionFloats milling_diameter; + ConfigOptionStrings milling_toolchange_end_gcode; + ConfigOptionStrings milling_toolchange_start_gcode; + //ConfigOptionPoints milling_offset; + //ConfigOptionFloats milling_z_offset; ConfigOptionInts min_fan_speed; ConfigOptionFloats min_layer_height; ConfigOptionFloats min_print_speed; @@ -1120,6 +1135,10 @@ protected: OPT_PTR(max_layer_height); OPT_PTR(max_print_height); OPT_PTR(milling_diameter); + OPT_PTR(milling_toolchange_end_gcode); + OPT_PTR(milling_toolchange_start_gcode); + //OPT_PTR(milling_offset); + //OPT_PTR(milling_z_offset); OPT_PTR(min_fan_speed); OPT_PTR(min_layer_height); OPT_PTR(min_print_speed); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 3e4eb9844..b8d89980a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -353,15 +353,30 @@ void PrintObject::make_perimeters() tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - m_print->throw_if_canceled(); - m_layers[layer_idx]->make_perimeters(); - } + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_perimeters(); } + } ); m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; + if (print()->config().milling_diameter.size() > 0 ) { + BOOST_LOG_TRIVIAL(debug) << "Generating milling post-process in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(0, m_layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_milling_post_process(); + } + } + ); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Generating milling post-process in parallel - end"; + } + this->set_done(posPerimeters); } @@ -1991,7 +2006,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full size_t num_extruders = print_config.nozzle_diameter.size(); object_config = object_config_from_model_object(object_config, model_object, num_extruders); - std::vector object_extruders; + std::vector object_extruders; for (const ModelVolume* model_volume : model_object.volumes) if (model_volume->is_model_part()) { PrintRegion::collect_object_printing_extruders( @@ -2017,9 +2032,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full } // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) -std::vector PrintObject::object_extruders() const +std::vector PrintObject::object_extruders() const { - std::vector extruders; + std::vector extruders; extruders.reserve(this->region_volumes.size() * 3); for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) if (! this->region_volumes[idx_region].empty()) diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index 5b94b3d56..c8c1537bb 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -3,7 +3,7 @@ namespace Slic3r { // 1-based extruder identifier for this region and role. -unsigned int PrintRegion::extruder(FlowRole role) const +uint16_t PrintRegion::extruder(FlowRole role) const { size_t extruder = 0; if (role == frPerimeter || role == frExternalPerimeter) @@ -65,7 +65,7 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.get_abs_value(1)); } -void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders) +void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintObjectConfig &object_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders) { // These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields. auto num_extruders = (int)print_config.nozzle_diameter.size(); @@ -81,7 +81,7 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con emplace_extruder(region_config.solid_infill_extruder); } -void PrintRegion::collect_object_printing_extruders(std::vector &object_extruders) const +void PrintRegion::collect_object_printing_extruders(std::vector &object_extruders) const { // PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder. // If not, then there must be something wrong with the Print::apply() function. diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index a2dad7d86..1a5308f90 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -62,7 +62,7 @@ SlicingParameters SlicingParameters::create_from_config( const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, - const std::vector &object_extruders) + const std::vector &object_extruders) { //first layer height is got from the first_layer_height setting unless the value was garbage. // if the first_layer_height setting depends of the nozzle width, use the first one. diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 2b7b7d49c..d49790e5d 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -31,7 +31,7 @@ struct SlicingParameters const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, - const std::vector &object_extruders); + const std::vector &object_extruders); // Has any raft layers? bool has_raft() const { return raft_layers() > 0; } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 99ca09c02..ad41bde55 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -418,6 +418,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) || config->option>("bottom_fill_pattern")->value == InfillPattern::ipSmooth || config->option>("solid_fill_pattern")->value == InfillPattern::ipSmooth)) || (config->option>("support_material_interface_pattern")->value == InfillPattern::ipSmooth && have_support_material)); + + //TODO: can the milling_diameter or the milling_cutter be check to enable/disable this? + for (auto el : { "milling_after_z", "milling_extra_size", "milling_speed" }) + toggle_field(el, config->opt_bool("milling_post_process")); + } void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 7318153ef..90d514575 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1426,8 +1426,8 @@ std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extru auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(print_z)); for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) { - const std::vector& extruders = it_layer_tools->extruders; - for (const auto& extruder : extruders) + const std::vector& extruders = it_layer_tools->extruders; + for (const uint16_t& extruder : extruders) used_extruders.emplace(extruder+1); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index b60bd957d..4491cb755 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -266,6 +266,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view _(L("Support material")) + "|" + _(L(width_screen == large? "Support material interface": "Sup. mat. interface")) + "|" + _(L("Wipe tower")) + "|" + + _(L("Mill")) + "|" + _(L("Custom")) ); Slic3r::GUI::create_combochecklist(m_combochecklist_features, feature_text, feature_items, true); @@ -323,6 +324,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view "Support material", "00FF00", "Support material interface", "008000", "Wipe tower", "B3E3AB", + "Mill", "B3B3B3", "Custom", "28CC94" }; m_gcode_preview_data->set_extrusion_paths_colors(extrusion_roles_colors); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 4ccd74220..8ca5c473d 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -530,7 +530,11 @@ const std::vector& Preset::print_options() , "curve_smoothing_angle_concave", "print_extrusion_multiplier", "external_perimeter_cut_corners", - "external_perimeter_overlap" + "external_perimeter_overlap", + "milling_after_z", + "milling_post_process", + "milling_extra_size", + "milling_speed", }; return s_opts; } -- cgit v1.2.3