Browse Source

Merge branch 'lm_improving_wipe_tower'. Summary of changes:
- wipe tower respects max volumetric flow, slowing down with PVA etc is not hardcoded anymore
- wipe tower is now allowed for multiple-extruder printers. single extruder stuff is not used in that case (ramming, cooling, etc.)
- start/end filament and toolchange custom gcodes are now inserted differently than before - see 41164a9
- some refactoring, e.g. the abstract WipeTower class was eradicated

Lukas Matena 5 years ago
parent
commit
e674c586b0

+ 1 - 2
src/libslic3r/CMakeLists.txt

@@ -81,9 +81,8 @@ add_library(libslic3r STATIC
     GCode/SpiralVase.hpp
     GCode/ToolOrdering.cpp
     GCode/ToolOrdering.hpp
+    GCode/WipeTower.cpp
     GCode/WipeTower.hpp
-    GCode/WipeTowerPrusaMM.cpp
-    GCode/WipeTowerPrusaMM.hpp
     GCode.cpp
     GCode.hpp
     GCodeReader.cpp

+ 166 - 95
src/libslic3r/GCode.cpp

@@ -4,7 +4,7 @@
 #include "EdgeGrid.hpp"
 #include "Geometry.hpp"
 #include "GCode/PrintExtents.hpp"
-#include "GCode/WipeTowerPrusaMM.hpp"
+#include "GCode/WipeTower.hpp"
 #include "Utils.hpp"
 
 #include <algorithm>
@@ -162,9 +162,9 @@ std::string Wipe::wipe(GCode &gcodegen, bool toolchange)
     return gcode;
 }
 
-static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt)
+static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2f &wipe_tower_pt)
 {
-    return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1)));
+    return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1)));
 }
 
 std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const
@@ -174,47 +174,97 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
     // Toolchangeresult.gcode assumes the wipe tower corner is at the origin
     // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
     float alpha = m_wipe_tower_rotation/180.f * float(M_PI);
-    WipeTower::xy start_pos = tcr.start_pos;
-    WipeTower::xy end_pos = tcr.end_pos;
-    start_pos.rotate(alpha);
-    start_pos.translate(m_wipe_tower_pos);
-    end_pos.rotate(alpha);
-    end_pos.translate(m_wipe_tower_pos);
-    std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
+    Vec2f start_pos = tcr.start_pos;
+    Vec2f end_pos = tcr.end_pos;
+    if (!tcr.priming) {
+        start_pos = Eigen::Rotation2Df(alpha) * start_pos;
+        start_pos += m_wipe_tower_pos;
+        end_pos = Eigen::Rotation2Df(alpha) * end_pos;
+        end_pos += m_wipe_tower_pos;
+    }
+    std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
     
 
     // Disable linear advance for the wipe tower operations.
     gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
-    // Move over the wipe tower.
-    // Retract for a tool change, using the toolchange retract value and setting the priming extra length.
-    gcode += gcodegen.retract(true);
-    gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
-    gcode += gcodegen.travel_to(
-        wipe_tower_point_to_object_point(gcodegen, start_pos),
-        erMixed,
-        "Travel to a Wipe Tower");
-    gcode += gcodegen.unretract();
-
-    // Let the tool change be executed by the wipe tower class.
-    // Inform the G-code writer about the changes done behind its back.
-    gcode += tcr_rotated_gcode;
-    // Let the m_writer know the current extruder_id, but ignore the generated G-code.
-	if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
-        gcodegen.writer().toolchange(new_extruder_id);
+
+    if (!tcr.priming) {
+        // Move over the wipe tower.
+        // Retract for a tool change, using the toolchange retract value and setting the priming extra length.
+        gcode += gcodegen.retract(true);
+        gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
+        gcode += gcodegen.travel_to(
+            wipe_tower_point_to_object_point(gcodegen, start_pos),
+            erMixed,
+            "Travel to a Wipe Tower");
+        gcode += gcodegen.unretract();
+    }
+
+
+    // Process the end filament gcode.
+    std::string end_filament_gcode_str;
+    if (gcodegen.writer().extruder() != nullptr) {
+        // Process the custom end_filament_gcode in case of single_extruder_multi_material.
+        unsigned int        old_extruder_id     = gcodegen.writer().extruder()->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()) {
+            end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
+            check_add_eol(end_filament_gcode_str);
+        }
+    }
+
+    // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament.
+    // Otherwise, leave control to the user completely.
+    std::string toolchange_gcode_str;
+    if (true /*gcodegen.writer().extruder() != nullptr*/) {
+        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;
+            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));
+            config.set_key_value("layer_z",           new ConfigOptionFloat(tcr.print_z));
+            toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config);
+            check_add_eol(toolchange_gcode_str);
+        }
+
+        std::string toolchange_command;
+        if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)))
+            toolchange_command = gcodegen.writer().toolchange(new_extruder_id);
+        if (toolchange_gcode.empty())
+            toolchange_gcode_str = toolchange_command;
+        else {
+            // We have informed the m_writer about the current extruder_id, we can ignore the generated G-code.
+        }
+    }
+
     gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
 
-    // Always append the filament start G-code even if the extruder did not switch,
-    // because the wipe tower resets the linear advance and we want it to be re-enabled.
+    // Process the start filament gcode.
+    std::string start_filament_gcode_str;
     const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id);
     if (! start_filament_gcode.empty()) {
         // Process the start_filament_gcode for the active filament only.
         DynamicConfig config;
         config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id));
-        gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
-        check_add_eol(gcode);
+        start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
+        check_add_eol(start_filament_gcode_str);
     }
+
+    // Insert the end filament, toolchange, and start filament gcode into the generated gcode.
+    DynamicConfig config;
+    config.set_key_value("end_filament_gcode",   new ConfigOptionString(end_filament_gcode_str));
+    config.set_key_value("toolchange_gcode",     new ConfigOptionString(toolchange_gcode_str));
+    config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str));
+    std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config);
+    unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode);
+    gcode += tcr_gcode;
+    check_add_eol(toolchange_gcode_str);
+
+
     // A phony move to the end position at the wipe tower.
-    gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y));
+    gcodegen.writer().travel_to_xy(end_pos.cast<double>());
     gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
 
     // Prepare a future wipe.
@@ -224,8 +274,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
         gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
         // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
         gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, 
-            WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
-            end_pos.y)));
+            Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left,
+            end_pos.y())));
     }
 
     // Let the planner know we are traveling between objects.
@@ -235,14 +285,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
 
 // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
 // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
-std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
+std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const
 {
     std::istringstream gcode_str(gcode_original);
     std::string gcode_out;
     std::string line;
-    WipeTower::xy pos = start_pos;
-    WipeTower::xy transformed_pos;
-    WipeTower::xy old_pos(-1000.1f, -1000.1f);
+    Vec2f pos = start_pos;
+    Vec2f transformed_pos;
+    Vec2f old_pos(-1000.1f, -1000.1f);
 
     while (gcode_str) {
         std::getline(gcode_str, line);  // we read the gcode line by line
@@ -253,25 +303,25 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
             char ch = 0;
             while (line_str >> ch) {
                 if (ch == 'X')
-                    line_str >> pos.x;
+                    line_str >> pos.x();
                 else
                     if (ch == 'Y')
-                        line_str >> pos.y;
+                        line_str >> pos.y();
                     else
                         line_out << ch;
             }
 
             transformed_pos = pos;
-            transformed_pos.rotate(angle);
-            transformed_pos.translate(translation);
+            transformed_pos = Eigen::Rotation2Df(angle) * transformed_pos;
+            transformed_pos += translation;
 
             if (transformed_pos != old_pos) {
                 line = line_out.str();
                 char buf[2048] = "G1";
-                if (transformed_pos.x != old_pos.x)
-                    sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
-                if (transformed_pos.y != old_pos.y)
-                    sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
+                if (transformed_pos.x() != old_pos.x())
+                    sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x());
+                if (transformed_pos.y() != old_pos.y())
+                    sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y());
 
                 line.replace(line.find("G1 "), 3, buf);
                 old_pos = transformed_pos;
@@ -288,27 +338,36 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
     assert(m_layer_idx == 0);
     std::string gcode;
 
-    if (&m_priming != nullptr && ! m_priming.extrusions.empty()) {
+    if (&m_priming != nullptr) {
         // Disable linear advance for the wipe tower operations.
-        gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
-        // Let the tool change be executed by the wipe tower class.
-        // Inform the G-code writer about the changes done behind its back.
-        gcode += m_priming.gcode;
-        // Let the m_writer know the current extruder_id, but ignore the generated G-code.
-        unsigned int current_extruder_id = m_priming.extrusions.back().tool;
-        gcodegen.writer().toolchange(current_extruder_id);
-        gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
+            //gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
+
+        for (const WipeTower::ToolChangeResult& tcr : m_priming) {
+            if (!tcr.extrusions.empty())
+                gcode += append_tcr(gcodegen, tcr, tcr.new_tool);
+
+
+            // Let the tool change be executed by the wipe tower class.
+            // Inform the G-code writer about the changes done behind its back.
+            //gcode += tcr.gcode;
+            // Let the m_writer know the current extruder_id, but ignore the generated G-code.
+      //      unsigned int current_extruder_id = tcr.extrusions.back().tool;
+      //      gcodegen.writer().toolchange(current_extruder_id);
+      //      gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
+
+        }
+
         // A phony move to the end position at the wipe tower.
-        gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y));
-        gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
+       /* gcodegen.writer().travel_to_xy(Vec2d(m_priming.back().end_pos.x, m_priming.back().end_pos.y));
+        gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
         // Prepare a future wipe.
         gcodegen.m_wipe.path.points.clear();
         // Start the wipe at the current position.
-        gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
+        gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
         // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
-        gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, 
-            WipeTower::xy((std::abs(m_left - m_priming.end_pos.x) < std::abs(m_right - m_priming.end_pos.x)) ? m_right : m_left,
-            m_priming.end_pos.y)));
+        gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
+            WipeTower::xy((std::abs(m_left - m_priming.back().end_pos.x) < std::abs(m_right - m_priming.back().end_pos.x)) ? m_right : m_left,
+            m_priming.back().end_pos.y)));*/
     }
     return gcode;
 }
@@ -807,24 +866,16 @@ void GCode::_do_export(Print &print, FILE *file)
 
     // Write the custom start G-code
     _writeln(file, start_gcode);
-    // Process filament-specific gcode in extruder order.
-    if (print.config().single_extruder_multi_material) {
-        if (has_wipe_tower) {
-            // Wipe tower will control the extruder switching, it will call the start_filament_gcode.
-        } else {
-            // Only initialize the initial extruder.
+
+    // Process filament-specific gcode.
+   /* if (has_wipe_tower) {
+        // Wipe tower will control the extruder switching, it will call the start_filament_gcode.
+    } else {
             DynamicConfig config;
             config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
-			_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
-        }
-    } else {
-        DynamicConfig config;
-        for (const std::string &start_gcode : print.config().start_filament_gcode.values) {
-            int extruder_id = (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front());
-            config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
-            _writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, extruder_id, &config));
-        }
+            _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
     }
+*/
     this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
     print.throw_if_canceled();
 
@@ -2563,7 +2614,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
             gcode += buf;
         }
 
-        if (m_last_mm3_per_mm != path.mm3_per_mm)
+        if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm))
         {
             m_last_mm3_per_mm = path.mm3_per_mm;
 
@@ -2741,38 +2792,58 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
     m_wipe.reset_path();
     
     if (m_writer.extruder() != nullptr) {
-        // Process the custom end_filament_gcode in case of single_extruder_multi_material.
+        // 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();
         const std::string  &end_filament_gcode  = m_config.end_filament_gcode.get_at(old_extruder_id);
-        if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) {
+        if (! end_filament_gcode.empty()) {
             gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
             check_add_eol(gcode);
         }
     }
 
-    m_placeholder_parser.set("current_extruder", extruder_id);
-
-    if (m_writer.extruder() != nullptr && ! m_config.toolchange_gcode.value.empty()) {
-        // Process the custom toolchange_gcode.
-        DynamicConfig config;
-        config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
-        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));
-        gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
-        check_add_eol(gcode);
-    }
     
     // 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)
         gcode += m_ooze_prevention.pre_toolchange(*this);
-    // Append the toolchange command.
-    gcode += m_writer.toolchange(extruder_id);
-    // Append the filament start G-code for single_extruder_multi_material.
+
+    const std::string& toolchange_gcode = m_config.toolchange_gcode.value;
+    if (m_writer.extruder() != nullptr) {
+        // 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()->id()));
+            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));
+            gcode += placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config);
+            check_add_eol(gcode);
+        }
+    }
+
+    // We inform the writer about what is happening, but we may not use the resulting gcode.
+    std::string toolchange_command = m_writer.toolchange(extruder_id);
+    if (toolchange_gcode.empty())
+        gcode += toolchange_command;
+    else {
+        // user provided his own toolchange gcode, no need to do anything
+    }
+
+    // Set the temperature if the wipe tower didn't (not needed for non-single extruder MM)
+    if (m_config.single_extruder_multi_material && !m_config.wipe_tower) {
+        int temp = (m_layer_index == 0 ? m_config.first_layer_temperature.get_at(extruder_id) :
+                                         m_config.temperature.get_at(extruder_id));
+
+        gcode += m_writer.set_temperature(temp, false);
+    }
+
+    m_placeholder_parser.set("current_extruder", extruder_id);
+
+    // Append the filament start G-code.
     const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
-    if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) {
-        // Process the start_filament_gcode for the active filament only.
+    if (! start_filament_gcode.empty()) {
+        // Process the start_filament_gcode for the new filament.
         gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
         check_add_eol(gcode);
     }

+ 4 - 4
src/libslic3r/GCode.hpp

@@ -83,7 +83,7 @@ class WipeTowerIntegration {
 public:
     WipeTowerIntegration(
         const PrintConfig                                           &print_config,
-        const WipeTower::ToolChangeResult                           &priming,
+        const std::vector<WipeTower::ToolChangeResult>              &priming,
         const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
         const WipeTower::ToolChangeResult                           &final_purge) :
         m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
@@ -108,15 +108,15 @@ private:
     std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
 
     // Postprocesses gcode: rotates and moves all G1 extrusions and returns result
-    std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
+    std::string rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const;
 
     // Left / right edges of the wipe tower, for the planning of wipe moves.
     const float                                                  m_left;
     const float                                                  m_right;
-    const WipeTower::xy                                          m_wipe_tower_pos;
+    const Vec2f                                                  m_wipe_tower_pos;
     const float                                                  m_wipe_tower_rotation;
     // Reference to cached values at the Printer class.
-    const WipeTower::ToolChangeResult                           &m_priming;
+    const std::vector<WipeTower::ToolChangeResult>              &m_priming;
     const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
     const WipeTower::ToolChangeResult                           &m_final_purge;
     // Current layer index.

+ 15 - 14
src/libslic3r/GCode/PrintExtents.cpp

@@ -149,8 +149,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
                 const WipeTower::Extrusion &e = tcr.extrusions[i];
                 if (e.width > 0) {
                     Vec2d delta = 0.5 * Vec2d(e.width, e.width);
-                    Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y);
-                    Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y);
+                    Vec2d p1 = trafo * (&e - 1)->pos.cast<double>();
+                    Vec2d p2 = trafo * e.pos.cast<double>();
                     bbox.merge(p1.cwiseMin(p2) - delta);
                     bbox.merge(p1.cwiseMax(p2) + delta);
                 }
@@ -165,18 +165,19 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
 {
     BoundingBoxf bbox;
     if (print.wipe_tower_data().priming != nullptr) {
-        const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming;
-        for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
-            const WipeTower::Extrusion &e = tcr.extrusions[i];
-            if (e.width > 0) {
-                Vec2d  p1((&e - 1)->pos.x, (&e - 1)->pos.y);
-                Vec2d  p2(e.pos.x, e.pos.y);
-                bbox.merge(p1);
-                coordf_t radius = 0.5 * e.width;
-                bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
-                bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
-                bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
-                bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
+        for (const WipeTower::ToolChangeResult &tcr : *print.wipe_tower_data().priming) {
+            for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
+                const WipeTower::Extrusion &e = tcr.extrusions[i];
+                if (e.width > 0) {
+                    const Vec2d& p1 = (&e - 1)->pos.cast<double>();
+                    const Vec2d& p2 = e.pos.cast<double>();
+                    bbox.merge(p1);
+                    coordf_t radius = 0.5 * e.width;
+                    bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
+                    bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
+                    bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
+                    bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
+                }
             }
         }
     }

File diff suppressed because it is too large
+ 285 - 283
src/libslic3r/GCode/WipeTower.cpp


+ 361 - 101
src/libslic3r/GCode/WipeTower.hpp

@@ -1,95 +1,30 @@
-#ifndef slic3r_WipeTower_hpp_
-#define slic3r_WipeTower_hpp_
+#ifndef WipeTower_
+#define WipeTower_
 
-#include <math.h>
-#include <utility>
+#include <cmath>
 #include <string>
-#include <vector>
-
-namespace Slic3r
-{
-
-// A pure virtual WipeTower definition.
-class WipeTower
-{
-public:
-	// Internal point class, to make the wipe tower independent from other slic3r modules.
-	// This is important for Prusa Research as we want to build the wipe tower post-processor independently from slic3r.
-	struct xy
-	{
-		xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
-		xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
-		xy  operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
-		xy  operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
-		xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
-		xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
-		bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
-		bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
-		
-		// Rotate the point around center of the wipe tower about given angle (in degrees)
-		xy rotate(float width, float depth, float angle) const {
-			xy out(0,0);
-			float temp_x = x - width / 2.f;
-			float temp_y = y - depth / 2.f;
-			angle *= float(M_PI/180.);
-			out.x += temp_x * cos(angle)  -  temp_y * sin(angle) + width / 2.f;
-			out.y += temp_x * sin(angle)  +  temp_y * cos(angle) + depth / 2.f;
-			return out;
-		}
-        
-        // Rotate the point around origin about given angle in degrees
-        void rotate(float angle) {
-            float temp_x = x * cos(angle)  -  y * sin(angle);
-			y = x * sin(angle)  +  y * cos(angle);
-            x = temp_x;
-		}
-
-        void translate(const xy& vect) {
-            x += vect.x;
-            y += vect.y;
-        }
+#include <sstream>
+#include <utility>
+#include <algorithm>
 
-		float x;
-		float y;
-	};
+#include "libslic3r/PrintConfig.hpp"
 
-	WipeTower() {}
-	virtual ~WipeTower() {}
 
-	// Return the wipe tower position.
-	virtual const xy& position() const = 0;
+namespace Slic3r
+{
 
-	// Return the wipe tower width.
-	virtual float     width() const = 0;
+class WipeTowerWriter;
 
-	// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
-	virtual bool 	  finished() const = 0;
 
-	// Switch to a next layer.
-	virtual void 	  set_layer(
-		// Print height of this layer.
-		float  print_z,
-		// Layer height, used to calculate extrusion the rate. 
-		float  layer_height, 
-		// Maximum number of tool changes on this layer or the layers below.
-		size_t max_tool_changes, 
-		// Is this the first layer of the print? In that case print the brim first.
-		bool   is_first_layer,
-		// Is this the last layer of the wipe tower?
-		bool   is_last_layer) = 0;
-
-	enum Purpose {
-		PURPOSE_MOVE_TO_TOWER,
-		PURPOSE_EXTRUDE,
-		PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE,
-	};
 
-	// Extrusion path of the wipe tower, for 3D preview of the generated tool paths.
-	struct Extrusion
+class WipeTower
+{
+public:
+    struct Extrusion
 	{
-		Extrusion(const xy &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
+		Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
 		// End position of this extrusion.
-		xy				pos;
+		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;
@@ -108,10 +43,10 @@ public:
 		std::vector<Extrusion> 	extrusions;
 		// Initial position, at which the wipe tower starts its action.
 		// At this position the extruder is loaded and there is no Z-hop applied.
-		xy						start_pos;
+		Vec2f						start_pos;
 		// Last point, at which the normal G-code generator of Slic3r shall continue.
 		// At this position the extruder is loaded and there is no Z-hop applied.
-		xy						end_pos;
+		Vec2f						end_pos;
 		// Time elapsed over this tool change.
 		// This is useful not only for the print time estimation, but also for the control of layer cooling.
 		float  				    elapsed_time;
@@ -119,50 +54,375 @@ public:
         // Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
         bool                    priming;
 
+        // Initial tool
+        int initial_tool;
+
+        // New tool
+        int new_tool;
+
 		// Sum the total length of the extrusion.
 		float total_extrusion_length_in_plane() {
 			float e_length = 0.f;
 			for (size_t i = 1; i < this->extrusions.size(); ++ i) {
 				const Extrusion &e = this->extrusions[i];
 				if (e.width > 0) {
-					xy v = e.pos - (&e - 1)->pos;
-					e_length += sqrt(v.x*v.x+v.y*v.y);
+					Vec2f v = e.pos - (&e - 1)->pos;
+					e_length += v.norm();
 				}
 			}
 			return e_length;
 		}
 	};
 
+	// x			-- x coordinates of wipe tower in mm ( left bottom corner )
+	// y			-- y coordinates of wipe tower in mm ( left bottom corner )
+	// width		-- width of wipe tower in mm ( default 60 mm - leave as it is )
+	// wipe_area	-- space available for one toolchange in mm
+	WipeTower(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
+              float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, 
+              float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
+              const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
+        m_semm(semm),
+        m_wipe_tower_pos(x, y),
+		m_wipe_tower_width(width),
+		m_wipe_tower_rotation_angle(rotation_angle),
+		m_y_shift(0.f),
+		m_z_pos(0.f),
+		m_is_first_layer(false),
+        m_gcode_flavor(flavor),
+        m_bridging(bridging),
+        m_current_tool(initial_tool),
+        wipe_volumes(wiping_matrix)
+        {
+            // If this is a single extruder MM printer, we will use all the SE-specific config values.
+            // Otherwise, the defaults will be used to turn off the SE stuff.
+            if (m_semm) {
+                m_cooling_tube_retraction = cooling_tube_retraction;
+                m_cooling_tube_length = cooling_tube_length;
+                m_parking_pos_retraction = parking_pos_retraction;
+                m_extra_loading_move = extra_loading_move;
+                m_set_extruder_trimpot = set_extruder_trimpot;
+            }
+        }
+
+	virtual ~WipeTower() {}
+
+
+	// Set the extruder properties.
+	void set_extruder(size_t idx, std::string material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
+                      float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
+                      float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float max_volumetric_speed, float nozzle_diameter)
+	{
+        //while (m_filpar.size() < idx+1)   // makes sure the required element is in the vector
+        m_filpar.push_back(FilamentParameters());
+
+        m_filpar[idx].material = material;
+        m_filpar[idx].temperature = temp;
+        m_filpar[idx].first_layer_temperature = first_layer_temp;
+
+        // If this is a single extruder MM printer, we will use all the SE-specific config values.
+        // Otherwise, the defaults will be used to turn off the SE stuff.
+        if (m_semm) {
+            m_filpar[idx].loading_speed           = loading_speed;
+            m_filpar[idx].loading_speed_start     = loading_speed_start;
+            m_filpar[idx].unloading_speed         = unloading_speed;
+            m_filpar[idx].unloading_speed_start   = unloading_speed_start;
+            m_filpar[idx].delay                   = delay;
+            m_filpar[idx].cooling_moves           = cooling_moves;
+            m_filpar[idx].cooling_initial_speed   = cooling_initial_speed;
+            m_filpar[idx].cooling_final_speed     = cooling_final_speed;
+        }
+
+        if (max_volumetric_speed != 0.f)
+            m_filpar[idx].max_e_speed = (max_volumetric_speed / Filament_Area);
+        m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
+
+        m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
+
+        std::stringstream stream{m_semm ? ramming_parameters : std::string()};
+        float speed = 0.f;
+        stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
+        m_filpar[idx].ramming_line_width_multiplicator /= 100;
+        m_filpar[idx].ramming_step_multiplicator /= 100;
+        while (stream >> speed)
+            m_filpar[idx].ramming_speed.push_back(speed);
+
+        m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
+	}
+
+
+	// 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);
+
+	// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
+	void generate(std::vector<std::vector<ToolChangeResult>> &result);
+
+    float get_depth() const { return m_wipe_tower_depth; }
+
+
+
+	// Switch to a next layer.
+	void set_layer(
+		// Print height of this layer.
+		float print_z,
+		// Layer height, used to calculate extrusion the rate.
+		float layer_height,
+		// Maximum number of tool changes on this layer or the layers below.
+		size_t max_tool_changes,
+		// Is this the first layer of the print? In that case print the brim first.
+		bool is_first_layer,
+		// Is this the last layer of the waste tower?
+		bool is_last_layer)
+	{
+		m_z_pos 				= print_z;
+		m_layer_height			= layer_height;
+		m_is_first_layer 		= is_first_layer;
+		m_print_brim = is_first_layer;
+		m_depth_traversed  = 0.f;
+		m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
+		if (is_first_layer) {
+            this->m_num_layer_changes 	= 0;
+            this->m_num_tool_changes 	= 0;
+        }
+        else
+            ++ m_num_layer_changes;
+		
+		// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
+		m_extrusion_flow = extrusion_flow(layer_height);
+
+        // Advance m_layer_info iterator, making sure we got it right
+		while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
+			++m_layer_info;
+	}
+
+	// Return the wipe tower position.
+	const Vec2f& 		 position() const { return m_wipe_tower_pos; }
+	// Return the wipe tower width.
+	float     		 width()    const { return m_wipe_tower_width; }
+	// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
+	bool 	  		 finished() const { return m_max_color_changes == 0; }
+
 	// Returns gcode to prime the nozzles at the front edge of the print bed.
-	virtual ToolChangeResult prime(
+	std::vector<ToolChangeResult> 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<unsigned int> &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) = 0;
+		bool 						last_wipe_inside_wipe_tower);
 
-	// Returns gcode for toolchange and the end position.
-	// if new_tool == -1, just unload the current filament over the wipe tower.
-	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 0;
+	// Returns gcode for a toolchange and a final print head position.
+	// On the first layer, extrude a brim around the future wipe tower first.
+	ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
 
-	// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
+	// Fill the unfilled space with a sparse infill.
 	// Call this method only if layer_finished() is false.
-	virtual ToolChangeResult finish_layer() = 0;
+	ToolChangeResult finish_layer();
+
+	// Is the current layer finished?
+	bool 			 layer_finished() const {
+		return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
+	}
+
+    std::vector<float> get_used_filament() const { return m_used_filament_length; }
+    int get_number_of_toolchanges() const { return m_num_tool_changes; }
+
+    struct FilamentParameters {
+        std::string 	    material = "PLA";
+        int  			    temperature = 0;
+        int  			    first_layer_temperature = 0;
+        float               loading_speed = 0.f;
+        float               loading_speed_start = 0.f;
+        float               unloading_speed = 0.f;
+        float               unloading_speed_start = 0.f;
+        float               delay = 0.f ;
+        int                 cooling_moves = 0;
+        float               cooling_initial_speed = 0.f;
+        float               cooling_final_speed = 0.f;
+        float               ramming_line_width_multiplicator = 0.f;
+        float               ramming_step_multiplicator = 0.f;
+        float               max_e_speed = std::numeric_limits<float>::max();
+        std::vector<float>  ramming_speed;
+        float               nozzle_diameter;
+    };
+
+private:
+	WipeTower();
+
+	enum wipe_shape // A fill-in direction
+	{
+		SHAPE_NORMAL = 1,
+		SHAPE_REVERSED = -1
+	};
+
+
+    const bool  m_peters_wipe_tower   = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
+    const float Filament_Area         = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
+    const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
+    const float WT_EPSILON            = 1e-3f;
+
+
+	bool   m_semm               = true; // Are we using a single extruder multimaterial printer?
+    Vec2f  m_wipe_tower_pos; 			// Left front corner of the wipe tower in mm.
+	float  m_wipe_tower_width; 			// Width of the wipe tower.
+	float  m_wipe_tower_depth 	= 0.f; 	// Depth of the wipe tower
+	float  m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
+    float  m_internal_rotation  = 0.f;
+	float  m_y_shift			= 0.f;  // y shift passed to writer
+	float  m_z_pos 				= 0.f;  // Current Z position.
+	float  m_layer_height 		= 0.f; 	// Current layer height.
+	size_t m_max_color_changes 	= 0; 	// Maximum number of color changes per layer.
+	bool   m_is_first_layer 	= false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
+    int    m_old_temperature    = -1;   // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
+
+	// G-code generator parameters.
+    float           m_cooling_tube_retraction   = 0.f;
+    float           m_cooling_tube_length       = 0.f;
+    float           m_parking_pos_retraction    = 0.f;
+    float           m_extra_loading_move        = 0.f;
+    float           m_bridging                  = 0.f;
+    bool            m_set_extruder_trimpot      = false;
+    bool            m_adhesion                  = true;
+    GCodeFlavor     m_gcode_flavor;
+
+	float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
+	float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
+
+	// Extruder specific parameters.
+    std::vector<FilamentParameters> m_filpar;
+
 
-	// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
-	// the wipe tower has been completely covered by the tool change extrusions,
-	// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
-	virtual bool 		     layer_finished() const = 0;
+	// 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.
+	bool m_print_brim = true;
+	// A fill-in direction (positive Y, negative Y) alternates with each layer.
+	wipe_shape   	m_current_shape = SHAPE_NORMAL;
+	unsigned int 	m_current_tool  = 0;
+    const std::vector<std::vector<float>> wipe_volumes;
 
-    // Returns used filament length per extruder:
-    virtual std::vector<float> get_used_filament() const = 0;
+	float           m_depth_traversed = 0.f; // Current y position at the wipe tower.
+	bool 			m_left_to_right   = true;
+	float			m_extra_spacing   = 1.f;
 
-    // Returns total number of toolchanges:
-    virtual int get_number_of_toolchanges() const = 0;
+	// Calculates extrusion flow needed to produce required line width for given layer height
+	float extrusion_flow(float layer_height = -1.f) const	// negative layer_height - return current m_extrusion_flow
+	{
+		if ( layer_height < 0 )
+			return m_extrusion_flow;
+		return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
+	}
+
+	// Calculates length of extrusion line to extrude given volume
+	float volume_to_length(float volume, float line_width, float layer_height) const {
+		return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
+	}
+
+	// Calculates depth for all layers and propagates them downwards
+	void plan_tower();
+
+	// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
+	void make_wipe_tower_square();
+
+    // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
+    void save_on_last_wipe();
+
+
+	struct box_coordinates
+	{
+		box_coordinates(float left, float bottom, float width, float height) :
+			ld(left        , bottom         ),
+			lu(left        , bottom + height),
+			rd(left + width, bottom         ),
+			ru(left + width, bottom + height) {}
+		box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {}
+		void translate(const Vec2f &shift) {
+			ld += shift; lu += shift;
+			rd += shift; ru += shift;
+		}
+		void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); }
+		void expand(const float offset) {
+			ld += Vec2f(- offset, - offset);
+			lu += Vec2f(- offset,   offset);
+			rd += Vec2f(  offset, - offset);
+			ru += Vec2f(  offset,   offset);
+		}
+		void expand(const float offset_x, const float offset_y) {
+			ld += Vec2f(- offset_x, - offset_y);
+			lu += Vec2f(- offset_x,   offset_y);
+			rd += Vec2f(  offset_x, - offset_y);
+			ru += Vec2f(  offset_x,   offset_y);
+		}
+		Vec2f ld;  // left down
+		Vec2f lu;	// left upper 
+		Vec2f rd;	// right lower
+		Vec2f ru;  // right upper
+	};
+
+
+	// to store information about tool changes for a given layer
+	struct WipeTowerInfo{
+		struct ToolChange {
+			unsigned int old_tool;
+			unsigned int new_tool;
+			float required_depth;
+            float ramming_depth;
+            float first_wipe_line;
+            float wipe_volume;
+			ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
+            : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
+		};
+		float z;		// z position of the layer
+		float height;	// layer height
+		float depth;	// depth of the layer based on all layers above
+		float extra_spacing;
+		float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
+
+		std::vector<ToolChange> tool_changes;
+
+		WipeTowerInfo(float z_par, float layer_height_par)
+			: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
+	};
+
+	std::vector<WipeTowerInfo> m_plan; 	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
+	std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
+
+    // Stores information about used filament length per extruder:
+    std::vector<float> m_used_filament_length;
+
+
+	// Returns gcode for wipe tower brim
+	// sideOnly			-- set to false -- experimental, draw brim on sides of wipe tower
+	// offset			-- set to 0		-- experimental, offset to replace brim in front / rear of wipe tower
+	ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
+
+	void toolchange_Unload(
+		WipeTowerWriter &writer,
+		const box_coordinates  &cleaning_box, 
+		const std::string&	 	current_material,
+		const int 				new_temperature);
+
+	void toolchange_Change(
+		WipeTowerWriter &writer,
+		const unsigned int		new_tool,
+		const std::string& 		new_material);
+	
+	void toolchange_Load(
+		WipeTowerWriter &writer,
+		const box_coordinates  &cleaning_box);
+	
+	void toolchange_Wipe(
+		WipeTowerWriter &writer,
+		const box_coordinates  &cleaning_box,
+		float wipe_volume);
 };
 
+
+
+
 }; // namespace Slic3r
 
-#endif /* slic3r_WipeTower_hpp_ */
+#endif // WipeTowerPrusaMM_hpp_ 

+ 0 - 388
src/libslic3r/GCode/WipeTowerPrusaMM.hpp

@@ -1,388 +0,0 @@
-#ifndef WipeTowerPrusaMM_hpp_
-#define WipeTowerPrusaMM_hpp_
-
-#include <cmath>
-#include <string>
-#include <sstream>
-#include <utility>
-#include <algorithm>
-
-#include "WipeTower.hpp"
-#include "PrintConfig.hpp"
-
-
-namespace Slic3r
-{
-
-namespace PrusaMultiMaterial {
-	class Writer;
-};
-
-
-
-class WipeTowerPrusaMM : public WipeTower
-{
-public:
-	enum material_type
-	{
-		INVALID = -1,
-		PLA   = 0,		// E:210C	B:55C
-		ABS   = 1,		// E:255C	B:100C
-		PET   = 2,		// E:240C	B:90C
-		HIPS  = 3,		// E:220C	B:100C
-		FLEX  = 4,		// E:245C	B:80C
-		SCAFF = 5,		// E:215C	B:55C
-		EDGE  = 6,		// E:240C	B:80C
-		NGEN  = 7,		// E:230C	B:80C
-		PVA   = 8,	    // E:210C	B:80C
-		PC    = 9
-	};
-
-	// Parse material name into material_type.
-	static material_type parse_material(const char *name);
-	static std::string   to_string(material_type material);
-
-	// x			-- x coordinates of wipe tower in mm ( left bottom corner )
-	// y			-- y coordinates of wipe tower in mm ( left bottom corner )
-	// width		-- width of wipe tower in mm ( default 60 mm - leave as it is )
-	// wipe_area	-- space available for one toolchange in mm
-	WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
-                     float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, 
-                     float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
-                     const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
-    m_wipe_tower_pos(x, y),
-		m_wipe_tower_width(width),
-		m_wipe_tower_rotation_angle(rotation_angle),
-		m_y_shift(0.f),
-		m_z_pos(0.f),
-		m_is_first_layer(false),
-        m_cooling_tube_retraction(cooling_tube_retraction),
-        m_cooling_tube_length(cooling_tube_length),
-        m_parking_pos_retraction(parking_pos_retraction),
-        m_extra_loading_move(extra_loading_move),
-		m_bridging(bridging),
-		m_set_extruder_trimpot(set_extruder_trimpot),
-        m_gcode_flavor(flavor),
-        m_current_tool(initial_tool),
-        wipe_volumes(wiping_matrix)
-        {}
-
-	virtual ~WipeTowerPrusaMM() {}
-
-
-	// Set the extruder properties.
-	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
-                      float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
-                      float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
-	{
-        //while (m_filpar.size() < idx+1)   // makes sure the required element is in the vector
-        m_filpar.push_back(FilamentParameters());
-
-        m_filpar[idx].material = material;
-        if (material == FLEX || material == SCAFF || material == PVA) {
-    		// MMU2 lowers the print speed using the speed override (M220) for printing of soluble PVA/BVOH and flex materials.
-    		// Therefore it does not make sense to use the new M220 B and M220 R (backup / restore).
-        	m_retain_speed_override = false;
-        }
-        m_filpar[idx].temperature = temp;
-        m_filpar[idx].first_layer_temperature = first_layer_temp;
-        m_filpar[idx].loading_speed = loading_speed;
-        m_filpar[idx].loading_speed_start = loading_speed_start;
-        m_filpar[idx].unloading_speed = unloading_speed;
-        m_filpar[idx].unloading_speed_start = unloading_speed_start;
-        m_filpar[idx].delay = delay;
-        m_filpar[idx].cooling_moves = cooling_moves;
-        m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
-        m_filpar[idx].cooling_final_speed = cooling_final_speed;
-        m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
-
-        m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
-
-        std::stringstream stream{ramming_parameters};
-        float speed = 0.f;
-        stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
-        m_filpar[idx].ramming_line_width_multiplicator /= 100;
-        m_filpar[idx].ramming_step_multiplicator /= 100;
-        while (stream >> speed)
-            m_filpar[idx].ramming_speed.push_back(speed);
-
-        m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
-	}
-
-
-	// 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);
-
-	// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
-	void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
-
-    float get_depth() const { return m_wipe_tower_depth; }
-
-
-
-	// Switch to a next layer.
-	virtual void set_layer(
-		// Print height of this layer.
-		float print_z,
-		// Layer height, used to calculate extrusion the rate.
-		float layer_height,
-		// Maximum number of tool changes on this layer or the layers below.
-		size_t max_tool_changes,
-		// Is this the first layer of the print? In that case print the brim first.
-		bool is_first_layer,
-		// Is this the last layer of the waste tower?
-		bool is_last_layer)
-	{
-		m_z_pos 				= print_z;
-		m_layer_height			= layer_height;
-		m_is_first_layer 		= is_first_layer;
-		m_print_brim = is_first_layer;
-		m_depth_traversed  = 0.f;
-		m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
-		if (is_first_layer) {
-            this->m_num_layer_changes 	= 0;
-            this->m_num_tool_changes 	= 0;
-        }
-        else
-            ++ m_num_layer_changes;
-		
-		// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
-		m_extrusion_flow = extrusion_flow(layer_height);
-
-        // Advance m_layer_info iterator, making sure we got it right
-		while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
-			++m_layer_info;
-	}
-
-	// Return the wipe tower position.
-	virtual const xy& 		 position() const { return m_wipe_tower_pos; }
-	// Return the wipe tower width.
-	virtual float     		 width()    const { return m_wipe_tower_width; }
-	// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
-	virtual bool 	  		 finished() const { return m_max_color_changes == 0; }
-
-	// Returns gcode to prime the nozzles at the front edge of the print bed.
-	virtual ToolChangeResult 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<unsigned int> &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);
-
-	// Returns gcode for a toolchange and a final print head position.
-	// On the first layer, extrude a brim around the future wipe tower first.
-	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
-
-	// Fill the unfilled space with a sparse infill.
-	// Call this method only if layer_finished() is false.
-	virtual ToolChangeResult finish_layer();
-
-	// Is the current layer finished?
-	virtual bool 			 layer_finished() const {
-		return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
-	}
-
-    virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
-    virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
-
-
-private:
-	WipeTowerPrusaMM();
-
-	enum wipe_shape // A fill-in direction
-	{
-		SHAPE_NORMAL = 1,
-		SHAPE_REVERSED = -1
-	};
-
-
-    const bool  m_peters_wipe_tower   = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
-    const float Filament_Area         = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
-    const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
-    const float WT_EPSILON            = 1e-3f;
-
-
-	xy 	   m_wipe_tower_pos; 			// Left front corner of the wipe tower in mm.
-	float  m_wipe_tower_width; 			// Width of the wipe tower.
-	float  m_wipe_tower_depth 	= 0.f; 	// Depth of the wipe tower
-	float  m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
-    float  m_internal_rotation  = 0.f;
-	float  m_y_shift			= 0.f;  // y shift passed to writer
-	float  m_z_pos 				= 0.f;  // Current Z position.
-	float  m_layer_height 		= 0.f; 	// Current layer height.
-	size_t m_max_color_changes 	= 0; 	// Maximum number of color changes per layer.
-	bool   m_is_first_layer 	= false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
-    int    m_old_temperature    = -1;   // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
-
-	// G-code generator parameters.
-    float           m_cooling_tube_retraction   = 0.f;
-    float           m_cooling_tube_length       = 0.f;
-    float           m_parking_pos_retraction    = 0.f;
-    float           m_extra_loading_move        = 0.f;
-    float           m_bridging                  = 0.f;
-    bool            m_set_extruder_trimpot      = false;
-    bool 			m_retain_speed_override		= true;
-    bool            m_adhesion                  = true;
-    GCodeFlavor     m_gcode_flavor;
-
-	float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
-	float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
-
-
-    struct FilamentParameters {
-        material_type 	    material = PLA;
-        int  			    temperature = 0;
-        int  			    first_layer_temperature = 0;
-        float               loading_speed = 0.f;
-        float               loading_speed_start = 0.f;
-        float               unloading_speed = 0.f;
-        float               unloading_speed_start = 0.f;
-        float               delay = 0.f ;
-        int                 cooling_moves = 0;
-        float               cooling_initial_speed = 0.f;
-        float               cooling_final_speed = 0.f;
-        float               ramming_line_width_multiplicator = 0.f;
-        float               ramming_step_multiplicator = 0.f;
-        std::vector<float>  ramming_speed;
-        float               nozzle_diameter;
-    };
-
-	// Extruder specific parameters.
-    std::vector<FilamentParameters> m_filpar;
-
-
-	// 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.
-	bool m_print_brim = true;
-	// A fill-in direction (positive Y, negative Y) alternates with each layer.
-	wipe_shape   	m_current_shape = SHAPE_NORMAL;
-	unsigned int 	m_current_tool  = 0;
-    const std::vector<std::vector<float>> wipe_volumes;
-
-	float           m_depth_traversed = 0.f; // Current y position at the wipe tower.
-	bool 			m_left_to_right   = true;
-	float			m_extra_spacing   = 1.f;
-
-	// Calculates extrusion flow needed to produce required line width for given layer height
-	float extrusion_flow(float layer_height = -1.f) const	// negative layer_height - return current m_extrusion_flow
-	{
-		if ( layer_height < 0 )
-			return m_extrusion_flow;
-		return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
-	}
-
-	// Calculates length of extrusion line to extrude given volume
-	float volume_to_length(float volume, float line_width, float layer_height) const {
-		return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
-	}
-
-	// Calculates depth for all layers and propagates them downwards
-	void plan_tower();
-
-	// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
-	void make_wipe_tower_square();
-
-    // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
-    void save_on_last_wipe();
-
-
-	struct box_coordinates
-	{
-		box_coordinates(float left, float bottom, float width, float height) :
-			ld(left        , bottom         ),
-			lu(left        , bottom + height),
-			rd(left + width, bottom         ),
-			ru(left + width, bottom + height) {}
-		box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {}
-		void translate(const xy &shift) {
-			ld += shift; lu += shift;
-			rd += shift; ru += shift;
-		}
-		void translate(const float dx, const float dy) { translate(xy(dx, dy)); }
-		void expand(const float offset) {
-			ld += xy(- offset, - offset);
-			lu += xy(- offset,   offset);
-			rd += xy(  offset, - offset);
-			ru += xy(  offset,   offset);
-		}
-		void expand(const float offset_x, const float offset_y) {
-			ld += xy(- offset_x, - offset_y);
-			lu += xy(- offset_x,   offset_y);
-			rd += xy(  offset_x, - offset_y);
-			ru += xy(  offset_x,   offset_y);
-		}
-		xy ld;  // left down
-		xy lu;	// left upper 
-		xy rd;	// right lower
-		xy ru;  // right upper
-	};
-
-
-	// to store information about tool changes for a given layer
-	struct WipeTowerInfo{
-		struct ToolChange {
-			unsigned int old_tool;
-			unsigned int new_tool;
-			float required_depth;
-            float ramming_depth;
-            float first_wipe_line;
-            float wipe_volume;
-			ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
-            : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
-		};
-		float z;		// z position of the layer
-		float height;	// layer height
-		float depth;	// depth of the layer based on all layers above
-		float extra_spacing;
-		float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
-
-		std::vector<ToolChange> tool_changes;
-
-		WipeTowerInfo(float z_par, float layer_height_par)
-			: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
-	};
-
-	std::vector<WipeTowerInfo> m_plan; 	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
-	std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
-
-    // Stores information about used filament length per extruder:
-    std::vector<float> m_used_filament_length;
-
-
-	// Returns gcode for wipe tower brim
-	// sideOnly			-- set to false -- experimental, draw brim on sides of wipe tower
-	// offset			-- set to 0		-- experimental, offset to replace brim in front / rear of wipe tower
-	ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
-
-	void toolchange_Unload(
-		PrusaMultiMaterial::Writer &writer,
-		const box_coordinates  &cleaning_box, 
-		const material_type	 	current_material,
-		const int 				new_temperature);
-
-	void toolchange_Change(
-		PrusaMultiMaterial::Writer &writer,
-		const unsigned int		new_tool,
-		material_type 			new_material);
-	
-	void toolchange_Load(
-		PrusaMultiMaterial::Writer &writer,
-		const box_coordinates  &cleaning_box);
-	
-	void toolchange_Wipe(
-		PrusaMultiMaterial::Writer &writer,
-		const box_coordinates  &cleaning_box,
-		float wipe_volume);
-};
-
-
-
-
-}; // namespace Slic3r
-
-#endif /* WipeTowerPrusaMM_hpp_ */

+ 14 - 14
src/libslic3r/Print.cpp

@@ -7,7 +7,7 @@
 #include "I18N.hpp"
 #include "SupportMaterial.hpp"
 #include "GCode.hpp"
-#include "GCode/WipeTowerPrusaMM.hpp"
+#include "GCode/WipeTower.hpp"
 #include "Utils.hpp"
 
 //#include "PrintExport.hpp"
@@ -93,7 +93,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
         "filament_density",
         "filament_notes",
         "filament_cost",
-        "filament_max_volumetric_speed",
         "first_layer_acceleration",
         "first_layer_bed_temperature",
         "first_layer_speed",
@@ -188,6 +187,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "filament_cooling_initial_speed"
             || opt_key == "filament_cooling_final_speed"
             || opt_key == "filament_ramming_parameters"
+            || opt_key == "filament_max_volumetric_speed"
             || opt_key == "gcode_flavor"
             || opt_key == "high_current_on_filament_swap"
             || opt_key == "infill_first"
@@ -1091,17 +1091,15 @@ std::string Print::validate() const
             return L("The Spiral Vase option can only be used when printing single material objects.");
     }
 
-    if (m_config.single_extruder_multi_material) {
-        for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
-            if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
-                return L("All extruders must have the same diameter for single extruder multimaterial printer.");
-    }
-
     if (this->has_wipe_tower() && ! m_objects.empty()) {
         if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin)
             return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
         if (! m_config.use_relative_e_distances)
             return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
+        
+        for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
+            if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
+                return L("All extruders must have the same diameter for the Wipe Tower.");
 
         if (m_objects.size() > 1) {
             bool                                has_custom_layering = false;
@@ -1610,7 +1608,6 @@ void Print::_make_brim()
 bool Print::has_wipe_tower() const
 {
     return 
-        m_config.single_extruder_multi_material.value && 
         ! m_config.spiral_vase.value &&
         m_config.wipe_tower.value && 
         m_config.nozzle_diameter.values.size() > 1;
@@ -1674,12 +1671,13 @@ void Print::_make_wipe_tower()
     this->throw_if_canceled();
 
     // Initialize the wipe tower.
-    WipeTowerPrusaMM wipe_tower(
+    WipeTower wipe_tower(
+        m_config.single_extruder_multi_material.value,
         float(m_config.wipe_tower_x.value),     float(m_config.wipe_tower_y.value), 
         float(m_config.wipe_tower_width.value),
         float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
         float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
-        float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging), 
+        float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
         m_config.high_current_on_filament_swap.value, m_config.gcode_flavor, wipe_volumes,
         m_wipe_tower_data.tool_ordering.first_extruder());
 
@@ -1688,9 +1686,10 @@ void Print::_make_wipe_tower()
 
     // Set the extruder & material properties at the wipe tower object.
     for (size_t i = 0; i < number_of_extruders; ++ i)
+
         wipe_tower.set_extruder(
             i, 
-            WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
+            m_config.filament_type.get_at(i),
             m_config.temperature.get_at(i),
             m_config.first_layer_temperature.get_at(i),
 			(float)m_config.filament_loading_speed.get_at(i),
@@ -1702,9 +1701,10 @@ void Print::_make_wipe_tower()
 			(float)m_config.filament_cooling_initial_speed.get_at(i),
 			(float)m_config.filament_cooling_final_speed.get_at(i),
             m_config.filament_ramming_parameters.get_at(i),
-			(float)m_config.nozzle_diameter.get_at(i));
+            m_config.filament_max_volumetric_speed.get_at(i),
+            m_config.nozzle_diameter.get_at(i));
 
-    m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
+    m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
         wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
 
     // Lets go through the wipe tower layers and determine pairs of extruder changes for each

+ 1 - 1
src/libslic3r/Print.hpp

@@ -216,7 +216,7 @@ struct WipeTowerData
     // Cache it here, so it does not need to be recalculated during the G-code generation.
     ToolOrdering                                          tool_ordering;
     // Cache of tool changes per print layer.
-    std::unique_ptr<WipeTower::ToolChangeResult>          priming;
+    std::unique_ptr<std::vector<WipeTower::ToolChangeResult>> priming;
     std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
     std::unique_ptr<WipeTower::ToolChangeResult>          final_purge;
     std::vector<float>                                    used_filament;

+ 11 - 11
src/slic3r/GUI/GLCanvas3D.cpp

@@ -2038,11 +2038,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
             // Should the wipe tower be visualized ?
             unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size();
 
-            bool semm = dynamic_cast<const ConfigOptionBool*>(m_config->option("single_extruder_multi_material"))->value;
             bool wt = dynamic_cast<const ConfigOptionBool*>(m_config->option("wipe_tower"))->value;
             bool co = dynamic_cast<const ConfigOptionBool*>(m_config->option("complete_objects"))->value;
 
-            if ((extruders_count > 1) && semm && wt && !co)
+            if ((extruders_count > 1) && wt && !co)
             {
                 // Height of a print (Show at least a slab)
                 double height = std::max(m_model->bounding_box().max(2), 10.0);
@@ -4617,7 +4616,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
     {
         const Print                 *print;
         const std::vector<float>    *tool_colors;
-        WipeTower::xy                wipe_tower_pos;
+        Vec2f                        wipe_tower_pos;
         float                        wipe_tower_angle;
 
         // Number of vertices (each vertex is 6x4=24 bytes long)
@@ -4648,12 +4647,13 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
     ctxt.print = print;
     ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
     if (print->wipe_tower_data().priming && print->config().single_extruder_multi_material_priming)
-        ctxt.priming.emplace_back(*print->wipe_tower_data().priming.get());
+        for (int i=0; i<print->wipe_tower_data().priming.get()->size(); ++i)
+            ctxt.priming.emplace_back(print->wipe_tower_data().priming.get()->at(i));
     if (print->wipe_tower_data().final_purge)
         ctxt.final.emplace_back(*print->wipe_tower_data().final_purge.get());
 
     ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI;
-    ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
+    ctxt.wipe_tower_pos = Vec2f(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
 
     BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
 
@@ -4715,19 +4715,19 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
                     WipeTower::Extrusion e_prev = extrusions.extrusions[i-1];
 
                     if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
-                        e_prev.pos.rotate(ctxt.wipe_tower_angle);
-                        e_prev.pos.translate(ctxt.wipe_tower_pos);
+                        e_prev.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e_prev.pos;
+                        e_prev.pos += ctxt.wipe_tower_pos;
                     }
 
                     for (; i < j; ++i) {
                         WipeTower::Extrusion e = extrusions.extrusions[i];
                         assert(e.width > 0.f);
                         if (!extrusions.priming) {
-                            e.pos.rotate(ctxt.wipe_tower_angle);
-                            e.pos.translate(ctxt.wipe_tower_pos);
+                            e.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e.pos;
+                            e.pos += ctxt.wipe_tower_pos;
                         }
 
-                        lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
+                        lines.emplace_back(Point::new_scale(e_prev.pos.x(), e_prev.pos.y()), Point::new_scale(e.pos.x(), e.pos.y()));
                         widths.emplace_back(e.width);
 
                         e_prev = e;
@@ -5271,7 +5271,7 @@ void GLCanvas3D::_load_fff_shells()
         double max_z = print->objects()[0]->model_object()->get_model()->bounding_box().max(2);
         const PrintConfig& config = print->config();
         unsigned int extruders_count = config.nozzle_diameter.size();
-        if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
+        if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) {
             float depth = print->get_wipe_tower_depth();
 
             // Calculate wipe tower brim spacing.

Some files were not shown because too many files changed in this diff