From a154fd34eef23353ced21643b2de43a360c037e2 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 16 Apr 2018 14:26:57 +0200
Subject: [PATCH 01/36] Added parameter extra_loading_move, prevented high
 feedrate moves during loading

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 19 ++++++++++++++-----
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  6 ++++--
 xs/src/libslic3r/Print.cpp                  |  4 +++-
 xs/src/libslic3r/PrintConfig.cpp            |  9 +++++++++
 xs/src/libslic3r/PrintConfig.hpp            |  2 ++
 xs/src/slic3r/GUI/Preset.cpp                |  2 +-
 xs/src/slic3r/GUI/Tab.cpp                   |  1 +
 7 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index ad7d91c50..bdafdfa23 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -792,9 +792,17 @@ void WipeTowerPrusaMM::toolchange_Unload(
     float xdist = std::abs(oldx-turning_point);
     float edist = -(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42);
     writer.suppress_preview()
-          .load_move_x(turning_point,-15    , 60.f * std::hypot(xdist,15)/15 * 83 )    // fixed speed after ramming
-          .load_move_x(oldx         ,edist  , 60.f * std::hypot(xdist,edist)/std::abs(edist) * m_filpar[m_current_tool].unloading_speed )
-          .load_move_x(turning_point,-15    , 60.f * std::hypot(xdist,15)/15       * m_filpar[m_current_tool].unloading_speed*0.55f )
+          .load_move_x(turning_point,-15    , 60.f * std::hypot(xdist,15)/15 * 83 );    // fixed speed after ramming
+
+    // now an ugly hack: unload the filament with a check that the x speed is 50 mm/s
+    const float speed = m_filpar[m_current_tool].unloading_speed;
+    xdist = std::min(xdist, std::abs( 50 * edist / speed ));
+    const float feedrate = std::abs( std::hypot(edist, xdist) / ((edist / speed) / 60.f));
+    writer.load_move_x(writer.x() + (m_left_to_right ? -1.f : 1.f) * xdist ,edist, feedrate );
+    xdist = std::abs(oldx-turning_point); // recover old value of xdist
+
+
+    writer.load_move_x(turning_point,-15    , 60.f * std::hypot(xdist,15)/15       * m_filpar[m_current_tool].unloading_speed*0.55f )
           .load_move_x(oldx         ,-12    , 60.f * std::hypot(xdist,12)/12       * m_filpar[m_current_tool].unloading_speed*0.35f )
           .resume_preview();
 
@@ -876,11 +884,12 @@ void WipeTowerPrusaMM::toolchange_Load(
     float loading_speed = m_filpar[m_current_tool].loading_speed; // mm/s in e axis
     float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
     float dist = std::abs(oldx-turning_point);
-    float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
+    //float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
+    float edist = m_parking_pos_retraction-50+m_extra_loading_move; // 50mm reserved for acceleration/deceleration
 	writer.append("; CP TOOLCHANGE LOAD\n")
 		  .suppress_preview()
 		  .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f)  // Acceleration
-		  .load_move_x(oldx,edist,60*std::hypot(dist,edist)/edist * loading_speed)             // Fast phase
+		  .load_move_x(oldx,edist,std::abs( 60*std::hypot(dist,edist)/edist * loading_speed) ) // Fast phase
 		  .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f)  // Slowing down
 		  .load_move_x(oldx, 10, 60*std::hypot(dist,10.f)/10.f * loading_speed*0.1f)           // Super slow
 		  .resume_preview();
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 175de0276..daaabdfc0 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -43,8 +43,8 @@ public:
 	// 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 bridging, const std::vector<float>& wiping_matrix,
-                     unsigned int initial_tool) :
+                     float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
+                     const 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),
@@ -54,6 +54,7 @@ public:
         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_current_tool(initial_tool)
  	{
@@ -197,6 +198,7 @@ private:
     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_adhesion                  = true;
 
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index c19c97fae..c12cb64cd 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -202,6 +202,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "parking_pos_retraction"
             || opt_key == "cooling_tube_retraction"
             || opt_key == "cooling_tube_length"
+            || opt_key == "extra_loading_move"
             || opt_key == "z_offset") {
             steps.emplace_back(psWipeTower);
         } else if (
@@ -1076,7 +1077,8 @@ void Print::_make_wipe_tower()
         float(this->config.wipe_tower_width.value),
         float(this->config.wipe_tower_rotation_angle.value), float(this->config.cooling_tube_retraction.value),
         float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value),
-        float(this->config.wipe_tower_bridging), wiping_volumes, m_tool_ordering.first_extruder());
+        float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wiping_volumes,
+        m_tool_ordering.first_extruder());
 
     //wipe_tower.set_retract();
     //wipe_tower.set_zhop();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 1e7e0bacc..75129f4fd 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1045,6 +1045,15 @@ PrintConfigDef::PrintConfigDef()
     def->min = 0;
     def->default_value = new ConfigOptionFloat(92.f);
 
+    def = this->add("extra_loading_move", coFloat);
+    def->label = L("Extra loading distance");
+    def->tooltip = L("When set to zero, the distance the filament is moved from parking position during load "
+                      "is exactly the same as it was moved back during unload. When positive, it is loaded further, "
+                      " if negative, the loading move is shorter than unloading. ");
+    def->sidetext = L("mm");
+    def->cli = "extra_loading_move=f";
+    def->default_value = new ConfigOptionFloat(-2.f);
+
     def = this->add("perimeter_acceleration", coFloat);
     def->label = L("Perimeters");
     def->tooltip = L("This is the acceleration your printer will use for perimeters. "
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 967a87310..62d8c7101 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -506,6 +506,7 @@ public:
     ConfigOptionFloat               cooling_tube_retraction;
     ConfigOptionFloat               cooling_tube_length;
     ConfigOptionFloat               parking_pos_retraction;
+    ConfigOptionFloat               extra_loading_move;
 
 
     std::string get_extrusion_axis() const
@@ -564,6 +565,7 @@ protected:
         OPT_PTR(cooling_tube_retraction);
         OPT_PTR(cooling_tube_length);
         OPT_PTR(parking_pos_retraction);
+        OPT_PTR(extra_loading_move);
     }
 };
 
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index d48c9bf8f..e4a4b2093 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -226,7 +226,7 @@ const std::vector<std::string>& Preset::printer_options()
             "octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
             "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
             "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
-            "cooling_tube_length", "parking_pos_retraction", "max_print_height", "default_print_profile", "inherits",
+            "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
         };
         s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
     }
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index cc4b18c7c..8ffe27351 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -1743,6 +1743,7 @@ void TabPrinter::build_extruder_pages(){
                 optgroup->append_single_option_line("cooling_tube_retraction");
                 optgroup->append_single_option_line("cooling_tube_length");
                 optgroup->append_single_option_line("parking_pos_retraction");
+                optgroup->append_single_option_line("extra_loading_move");
             m_pages.insert(m_pages.begin()+1,page);
         }
     }

From 8c77b9645c698dd850703f3f4dbfe51dc441b82c Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 24 Apr 2018 13:02:08 +0200
Subject: [PATCH 02/36] Loading, unloading and cooling reworked, new filament
 parameters regarding cooling were added

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 101 ++++++++++----------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  11 ++-
 xs/src/libslic3r/Print.cpp                  |   8 +-
 xs/src/libslic3r/PrintConfig.cpp            |  32 +++++--
 xs/src/libslic3r/PrintConfig.hpp            |   8 +-
 xs/src/slic3r/GUI/Preset.cpp                |   8 +-
 xs/src/slic3r/GUI/Tab.cpp                   |   4 +-
 7 files changed, 99 insertions(+), 73 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index bdafdfa23..db6e93362 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -219,6 +219,17 @@ public:
 	Writer& retract(float e, float f = 0.f)
 		{ return load(-e, f); }
 
+// Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary)
+    Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f)
+    {
+        float time = std::abs(loading_dist / loading_speed);
+        float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time);
+        float feedrate = 60.f * std::hypot(x_speed, loading_speed);
+
+        float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time;
+        return extrude_explicit(end_point, y(), loading_dist, feedrate);
+    }
+
 	// Elevate the extruder head above the current print_z position.
 	Writer& z_hop(float hop, float f = 0.f)
 	{ 
@@ -786,58 +797,43 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	}
 	WipeTower::xy end_of_ramming(writer.x(),writer.y());
 
-    // Pull the filament end to the BEGINNING of the cooling tube while still moving the print head
-    float oldx = writer.x();
-    float turning_point = (!m_left_to_right ? std::max(xl,oldx-15.f) : std::min(xr,oldx+15.f) ); // so it's not too far
-    float xdist = std::abs(oldx-turning_point);
-    float edist = -(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42);
+
+    // Retraction:
+    float old_x = writer.x();
+    float turning_point = (!m_left_to_right ? xl : xr );
+    float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
     writer.suppress_preview()
-          .load_move_x(turning_point,-15    , 60.f * std::hypot(xdist,15)/15 * 83 );    // fixed speed after ramming
-
-    // now an ugly hack: unload the filament with a check that the x speed is 50 mm/s
-    const float speed = m_filpar[m_current_tool].unloading_speed;
-    xdist = std::min(xdist, std::abs( 50 * edist / speed ));
-    const float feedrate = std::abs( std::hypot(edist, xdist) / ((edist / speed) / 60.f));
-    writer.load_move_x(writer.x() + (m_left_to_right ? -1.f : 1.f) * xdist ,edist, feedrate );
-    xdist = std::abs(oldx-turning_point); // recover old value of xdist
-
-
-    writer.load_move_x(turning_point,-15    , 60.f * std::hypot(xdist,15)/15       * m_filpar[m_current_tool].unloading_speed*0.55f )
-          .load_move_x(oldx         ,-12    , 60.f * std::hypot(xdist,12)/12       * m_filpar[m_current_tool].unloading_speed*0.35f )
+          .load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
+          .load_move_x_advanced(old_x,         -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
+          .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
+          .load_move_x_advanced(old_x,         -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
+          .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
           .resume_preview();
 
-	if (new_temperature != 0) 	// Set the extruder temperature, but don't wait.
+    if (new_temperature != 0) 	// Set the extruder temperature, but don't wait.
 		writer.set_extruder_temp(new_temperature, false);
 
-// cooling:
-	writer.suppress_preview();
-	writer.travel(writer.x(), writer.y() + y_step);
-	const float start_x = writer.x();
-	turning_point = ( xr-start_x > start_x-xl ? xr : xl );
-	const float max_x_dist = 2*std::abs(start_x-turning_point);
-	const unsigned int N = 4 + std::max(0.f, (m_filpar[m_current_tool].cooling_time-14)/3);
-	float time = m_filpar[m_current_tool].cooling_time / float(N);
+    // Cooling:
+    const unsigned number_of_moves = 3;
+    if (number_of_moves > 0) {
+        const float initial_speed = 2.2f;   // mm/s
+        const float final_speed   = 3.4f;
 
-	i = 0;
-	while (i<N) {
-		const float speed = std::min(3.4,2.2 + i*0.3 + (i==0 ? 0 : 0.3)); // mm per second: 2.2, 2.8, 3.1, 3.4, 3.4, 3.4, ...		
-		const float e_dist = std::min(speed * time,2*m_cooling_tube_length); // distance to travel
-		
-		// this move is the last one at this speed or someone set tube_length to zero
-        if (speed * time < 2*m_cooling_tube_length || m_cooling_tube_length<WT_EPSILON) {
-            ++i;
-			time = m_filpar[m_current_tool].cooling_time / float(N);
-		}
-		else
-			time -= e_dist / speed; // subtract time this part will really take
+        float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
 
-		// as for x, we will make sure the feedrate is at most 2000
-		float x_dist = (turning_point - WT_EPSILON < xl ? -1.f : 1.f) * std::min(e_dist * (float)sqrt(pow(2000 / (60 * speed), 2) - 1),max_x_dist);
-		const float feedrate = std::hypot(e_dist, x_dist) / ((e_dist / speed) / 60.f);
-		writer.cool(start_x+x_dist/2.f,start_x,e_dist/2.f,-e_dist/2.f, feedrate);
-	}
+        writer.suppress_preview()
+              .travel(writer.x(), writer.y() + y_step);
+        old_x = writer.x();
+        turning_point = xr-old_x > old_x-xl ? xr : xl;
+        for (unsigned i=0; i<number_of_moves; ++i) {
+            float speed = initial_speed + speed_inc * 2*i;
+            writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
+            speed += speed_inc;
+            writer.load_move_x_advanced(old_x, -m_cooling_tube_length, speed);
+        }
+    }
 
-    // let's wait is necessary
+    // let's wait is necessary:
     writer.wait(m_filpar[m_current_tool].delay);
     // we should be at the beginning of the cooling tube again - let's move to parking position:
     writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
@@ -881,17 +877,16 @@ void WipeTowerPrusaMM::toolchange_Load(
 	float oldx = writer.x();	// the nozzle is in place to do the first wiping moves, we will remember the position
 
     // Load the filament while moving left / right, so the excess material will not create a blob at a single position.
-    float loading_speed = m_filpar[m_current_tool].loading_speed; // mm/s in e axis
     float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
-    float dist = std::abs(oldx-turning_point);
-    //float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
-    float edist = m_parking_pos_retraction-50+m_extra_loading_move; // 50mm reserved for acceleration/deceleration
-	writer.append("; CP TOOLCHANGE LOAD\n")
+    float edist = m_parking_pos_retraction+m_extra_loading_move;
+
+    writer.append("; CP TOOLCHANGE LOAD\n")
 		  .suppress_preview()
-		  .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f)  // Acceleration
-		  .load_move_x(oldx,edist,std::abs( 60*std::hypot(dist,edist)/edist * loading_speed) ) // Fast phase
-		  .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f)  // Slowing down
-		  .load_move_x(oldx, 10, 60*std::hypot(dist,10.f)/10.f * loading_speed*0.1f)           // Super slow
+		  .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed)  // Acceleration
+		  .load_move_x_advanced(oldx,          0.5f * edist,        m_filpar[m_current_tool].loading_speed)  // Fast phase
+		  .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed)  // Slowing down
+		  .load_move_x_advanced(oldx,          0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed)  // Super slow
+          .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
 		  .resume_preview();
 
 	// Reset the extruder current to the normal value.
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index daaabdfc0..6744aa917 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -68,7 +68,8 @@ public:
 
 	// Set the extruder properties.
 	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
-                      float unloading_speed, float delay, float cooling_time, std::string ramming_parameters, float nozzle_diameter)
+                      float unloading_speed, 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());
@@ -79,7 +80,9 @@ public:
         m_filpar[idx].loading_speed = loading_speed;
         m_filpar[idx].unloading_speed = unloading_speed;
         m_filpar[idx].delay = delay;
-        m_filpar[idx].cooling_time = cooling_time;
+        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
@@ -213,7 +216,9 @@ private:
         float               loading_speed = 0.f;
         float               unloading_speed = 0.f;
         float               delay = 0.f ;
-        float               cooling_time = 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;
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index c12cb64cd..ec91691a1 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -186,7 +186,9 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "filament_loading_speed"
             || opt_key == "filament_unloading_speed"
             || opt_key == "filament_toolchange_delay"
-            || opt_key == "filament_cooling_time"
+            || opt_key == "filament_cooling_moves"
+            || opt_key == "filament_cooling_initial_speed"
+            || opt_key == "filament_cooling_final_speed"
             || opt_key == "filament_ramming_parameters"
             || opt_key == "gcode_flavor"
             || opt_key == "single_extruder_multi_material"
@@ -1093,7 +1095,9 @@ void Print::_make_wipe_tower()
             this->config.filament_loading_speed.get_at(i),
             this->config.filament_unloading_speed.get_at(i),
             this->config.filament_toolchange_delay.get_at(i),
-            this->config.filament_cooling_time.get_at(i),
+            this->config.filament_cooling_moves.get_at(i),
+            this->config.filament_cooling_initial_speed.get_at(i),
+            this->config.filament_cooling_final_speed.get_at(i),
             this->config.filament_ramming_parameters.get_at(i),
             this->config.nozzle_diameter.get_at(i));
 
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 75129f4fd..55a1b84e3 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -481,15 +481,31 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "filament-toolchange-delay=f@";
     def->min = 0;
     def->default_value = new ConfigOptionFloats { 0. };
-    
-    def = this->add("filament_cooling_time", coFloats);
-    def->label = L("Cooling time");
-    def->tooltip = L("The filament is slowly moved back and forth after retraction into the cooling tube "
-                   "for this amount of time.");
-    def->cli = "filament_cooling_time=i@";
-    def->sidetext = L("s");
+
+    def = this->add("filament_cooling_moves", coInts);
+    def->label = L("Number of cooling moves");
+    def->tooltip = L("Filament is cooled by being moved back and forth in the "
+                   "cooling tubes. Specify desired number of these moves ");
+    def->cli = "filament-cooling-moves=i@";
+    def->max = 0;
+    def->max = 20;
+    def->default_value = new ConfigOptionInts { 6 };
+
+    def = this->add("filament_cooling_initial_speed", coFloats);
+    def->label = L("Speed of the first cooling move");
+    def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed. ");
+    def->cli = "filament-cooling-initial-speed=i@";
+    def->sidetext = L("mm/s");
     def->min = 0;
-    def->default_value = new ConfigOptionFloats { 14.f };
+    def->default_value = new ConfigOptionFloats { 2.2f };
+
+    def = this->add("filament_cooling_final_speed", coFloats);
+    def->label = L("Speed of the last cooling move");
+    def->tooltip = L("Cooling moves are gradually accelerating towards this speed. ");
+    def->cli = "filament-cooling-final-speed=i@";
+    def->sidetext = L("mm/s");
+    def->min = 0;
+    def->default_value = new ConfigOptionFloats { 3.4f };
 
     def = this->add("filament_ramming_parameters", coStrings);
     def->label = L("Ramming parameters");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 62d8c7101..a36e5def9 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -476,7 +476,9 @@ public:
     ConfigOptionFloats              filament_loading_speed;
     ConfigOptionFloats              filament_unloading_speed;
     ConfigOptionFloats              filament_toolchange_delay;
-    ConfigOptionFloats              filament_cooling_time;
+    ConfigOptionInts                filament_cooling_moves;
+    ConfigOptionFloats              filament_cooling_initial_speed;
+    ConfigOptionFloats              filament_cooling_final_speed;
     ConfigOptionStrings             filament_ramming_parameters;
     ConfigOptionBool                gcode_comments;
     ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
@@ -535,7 +537,9 @@ protected:
         OPT_PTR(filament_loading_speed);
         OPT_PTR(filament_unloading_speed);
         OPT_PTR(filament_toolchange_delay);
-        OPT_PTR(filament_cooling_time);
+        OPT_PTR(filament_cooling_moves);
+        OPT_PTR(filament_cooling_initial_speed);
+        OPT_PTR(filament_cooling_final_speed);
         OPT_PTR(filament_ramming_parameters);
         OPT_PTR(gcode_comments);
         OPT_PTR(gcode_flavor);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index e4a4b2093..db2fdfc17 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -209,10 +209,10 @@ const std::vector<std::string>& Preset::filament_options()
     static std::vector<std::string> s_opts {
         "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
         "extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_unloading_speed", "filament_toolchange_delay",
-        "filament_cooling_time", "filament_ramming_parameters", "temperature", "first_layer_temperature", "bed_temperature",
-        "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers",
-        "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers",
-        "compatible_printers_condition", "inherits"
+        "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "temperature",
+        "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed",
+        "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
+        "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits"
     };
     return s_opts;
 }
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 8ffe27351..da5b1b064 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -1287,7 +1287,9 @@ void TabFilament::build()
 		optgroup->append_single_option_line("filament_loading_speed");
         optgroup->append_single_option_line("filament_unloading_speed");
         optgroup->append_single_option_line("filament_toolchange_delay");
-        optgroup->append_single_option_line("filament_cooling_time");
+        optgroup->append_single_option_line("filament_cooling_moves");
+        optgroup->append_single_option_line("filament_cooling_initial_speed");
+        optgroup->append_single_option_line("filament_cooling_final_speed");
         line = { _(L("Ramming")), "" };
         line.widget = [this](wxWindow* parent){
 			auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);

From 650489dd8a99e662d4f029c1ff82e6c29bba01c2 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 24 Apr 2018 13:43:39 +0200
Subject: [PATCH 03/36] New parameters actually connected to the wipe tower
 generator

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 8 ++++----
 xs/src/libslic3r/PrintConfig.cpp            | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index db6e93362..80d4fdf07 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -814,10 +814,10 @@ void WipeTowerPrusaMM::toolchange_Unload(
 		writer.set_extruder_temp(new_temperature, false);
 
     // Cooling:
-    const unsigned number_of_moves = 3;
+    const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
     if (number_of_moves > 0) {
-        const float initial_speed = 2.2f;   // mm/s
-        const float final_speed   = 3.4f;
+        const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
+        const float& final_speed   = m_filpar[m_current_tool].cooling_final_speed;
 
         float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
 
@@ -825,7 +825,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
               .travel(writer.x(), writer.y() + y_step);
         old_x = writer.x();
         turning_point = xr-old_x > old_x-xl ? xr : xl;
-        for (unsigned i=0; i<number_of_moves; ++i) {
+        for (int i=0; i<number_of_moves; ++i) {
             float speed = initial_speed + speed_inc * 2*i;
             writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
             speed += speed_inc;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 55a1b84e3..8ba0e7cd6 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -489,7 +489,7 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "filament-cooling-moves=i@";
     def->max = 0;
     def->max = 20;
-    def->default_value = new ConfigOptionInts { 6 };
+    def->default_value = new ConfigOptionInts { 4 };
 
     def = this->add("filament_cooling_initial_speed", coFloats);
     def->label = L("Speed of the first cooling move");

From 24dc4c0f236d53d48d81bca11b1ef6d0de3fa511 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 26 Apr 2018 11:19:51 +0200
Subject: [PATCH 04/36] Yet another attempt to fix the layer height profile
 validation

---
 xs/src/libslic3r/Print.cpp | 35 ++++++++++++++++++++++-------------
 1 file changed, 22 insertions(+), 13 deletions(-)

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index ec91691a1..38a41370b 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -602,10 +602,10 @@ std::string Print::validate() const
             return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
         SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
 
-        const PrintObject* most_layered_object = this->objects.front(); // object with highest layer_height_profile.size() encountered so far
+        const PrintObject* tallest_object = this->objects.front(); // let's find the tallest object
         for (const auto* object : objects)
-            if (object->layer_height_profile.size() > most_layered_object->layer_height_profile.size())
-                most_layered_object = object;
+            if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
+                    tallest_object = object;
 
         for (PrintObject *object : this->objects) {
             SlicingParameters slicing_params = object->slicing_parameters();
@@ -622,17 +622,26 @@ std::string Print::validate() const
             object->update_layer_height_profile();
             object->layer_height_profile_valid = was_layer_height_profile_valid;
 
-            if ( this->config.variable_layer_height ) {
-                int i = 0;
-                while ( i < object->layer_height_profile.size() ) {
-                    if (std::abs(most_layered_object->layer_height_profile[i] - object->layer_height_profile[i]) > EPSILON)
-                        return "The Wipe tower is only supported if all objects have the same layer height profile";
-                    ++i;
-                    if (i == object->layer_height_profile.size()-2) // this element contains the objects max z, if the other object is taller,
-                                                                    // it does not have to match - we will step over it
-                        if (most_layered_object->layer_height_profile[i] > object->layer_height_profile[i])
-                            ++i;
+            if ( this->config.variable_layer_height ) { // comparing layer height profiles
+                bool failed = false;
+                if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
+                    int i = 0;
+                    while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
+                        if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
+                            failed = true;
+                            break;
+                        }
+                        ++i;
+                        if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
+                            if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case
+                                ++i;
+                    }
                 }
+                else
+                    failed = true;
+
+                if (failed)
+                    return "The Wipe tower is only supported if all objects have the same layer height profile";
             }
 
             /*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)

From 71b43370360ddef5a0dcded7358e0dcf13268bef Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 2 May 2018 10:52:17 +0200
Subject: [PATCH 05/36] Label in filament settings changed

---
 xs/src/slic3r/GUI/Tab.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 8d449b7f0..2f3a8f00e 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -1283,7 +1283,7 @@ void TabFilament::build()
 		};
 		optgroup->append_line(line);
 
-        optgroup = page->new_optgroup(_(L("Toolchange behaviour")));
+        optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
 		optgroup->append_single_option_line("filament_loading_speed");
         optgroup->append_single_option_line("filament_unloading_speed");
         optgroup->append_single_option_line("filament_toolchange_delay");

From b6db3767a2dfca86cbf275e543da6ce00d035f91 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 11 May 2018 17:35:42 +0200
Subject: [PATCH 06/36] Bugfix: extruder temperature only changes when the
 temperature differs from the one last set (wipe tower)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 17 +++++++----------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  1 +
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 80d4fdf07..f328d839f 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -275,12 +275,9 @@ public:
 	// Set extruder temperature, don't wait by default.
 	Writer& set_extruder_temp(int temperature, bool wait = false)
 	{
-        if (temperature != current_temp) {
-            char buf[128];
-            sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
-            m_gcode += buf;
-            current_temp = temperature;
-        }
+        char buf[128];
+        sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
+        m_gcode += buf;
         return *this;
 	};
 
@@ -395,10 +392,8 @@ private:
 	float 		  m_wipe_tower_width = 0.f;
 	float		  m_wipe_tower_depth = 0.f;
 	float		  m_last_fan_speed = 0.f;
-    int           current_temp = -1;
 
-		std::string
-		set_format_X(float x)
+	std::string   set_format_X(float x)
 	{
 		char buf[64];
 		sprintf(buf, " X%.3f", x);
@@ -810,8 +805,10 @@ void WipeTowerPrusaMM::toolchange_Unload(
           .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
           .resume_preview();
 
-    if (new_temperature != 0) 	// Set the extruder temperature, but don't wait.
+    if (new_temperature != 0 && new_temperature != m_old_temperature ) { 	// Set the extruder temperature, but don't wait.
 		writer.set_extruder_temp(new_temperature, false);
+        m_old_temperature = new_temperature;
+    }
 
     // Cooling:
     const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 6744aa917..ea1c1f631 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -196,6 +196,7 @@ private:
 	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;

From 95795f249afc63da16a1cb901876e758259f4e09 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 24 May 2018 14:05:51 +0200
Subject: [PATCH 07/36] First steps in reorganizing infill order (to use infill
 instead of the wipe tower)

---
 xs/src/libslic3r/ExtrusionEntity.hpp          |  4 +++
 .../libslic3r/ExtrusionEntityCollection.cpp   |  1 +
 .../libslic3r/ExtrusionEntityCollection.hpp   | 16 +++++++++
 xs/src/libslic3r/GCode.cpp                    | 36 ++++++++++++++++---
 xs/src/libslic3r/Print.cpp                    | 20 +++++++++++
 5 files changed, 73 insertions(+), 4 deletions(-)

diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 16ef51c1f..15363e8ed 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -92,6 +92,7 @@ public:
     virtual double min_mm3_per_mm() const = 0;
     virtual Polyline as_polyline() const = 0;
     virtual double length() const = 0;
+    virtual double total_volume() const = 0;
 };
 
 typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
@@ -148,6 +149,7 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const { return this->mm3_per_mm; }
     Polyline as_polyline() const { return this->polyline; }
+    virtual double total_volume() const { return mm3_per_mm * unscale(length()); }
 
 private:
     void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -194,6 +196,7 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const;
     Polyline as_polyline() const;
+    virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
 };
 
 // Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -241,6 +244,7 @@ public:
     // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
     double min_mm3_per_mm() const;
     Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
+    virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
 
 private:
     ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
index 4513139e2..7a086bcbf 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
@@ -125,6 +125,7 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
                 continue;
             }
         }
+
         ExtrusionEntity* entity = (*it)->clone();
         my_paths.push_back(entity);
         if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin();
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 03bd2ba97..d292248fc 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -79,6 +79,7 @@ public:
     void flatten(ExtrusionEntityCollection* retval) const;
     ExtrusionEntityCollection flatten() const;
     double min_mm3_per_mm() const;
+    virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
 
     // Following methods shall never be called on an ExtrusionEntityCollection.
     Polyline as_polyline() const {
@@ -89,6 +90,21 @@ public:
         CONFESS("Calling length() on a ExtrusionEntityCollection");
         return 0.;        
     }
+
+    void set_extruder_override(int extruder) {
+        extruder_override = extruder;
+        for (auto& member : entities) {
+            if (member->is_collection())
+                dynamic_cast<ExtrusionEntityCollection*>(member)->set_extruder_override(extruder);
+        }
+    }
+    int get_extruder_override() const { return extruder_override; }
+    bool is_extruder_overridden() const { return extruder_override != -1; }
+
+
+private:
+    // Set this variable to explicitly state you want to use specific extruder for thie EEC (used for MM infill wiping)
+    int extruder_override = -1;
 };
 
 }
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index b581b3e76..3536c0c9c 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1249,7 +1249,7 @@ void GCode::process_layer(
                             break;
                         }
                 }
-                
+
                 // process infill
                 // layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection), 
                 // each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface"
@@ -1261,6 +1261,10 @@ void GCode::process_layer(
                     if (fill->entities.empty())
                         // This shouldn't happen but first_point() would fail.
                         continue;
+
+                    if (fill->is_extruder_overridden())
+                        continue;
+
                     // init by_extruder item only if we actually use the extruder
                     int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1);
                     // Init by_extruder item only if we actually use the extruder.
@@ -1334,15 +1338,37 @@ void GCode::process_layer(
             m_avoid_crossing_perimeters.disable_once = true;
         }
 
+        for (const auto& layer_to_print : layers) {   // iterate through all objects
+            if (layer_to_print.object_layer == nullptr)
+                continue;
+            std::vector<ObjectByExtruder::Island::Region> overridden;
+            for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
+                ObjectByExtruder::Island::Region new_region;
+                overridden.push_back(new_region);
+                for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
+                    auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                    if (fill->get_extruder_override() == extruder_id) {
+                        overridden.back().infills.append(*fill);
+                        fill->set_extruder_override(-1);
+                    }
+               }
+                m_config.apply((layer_to_print.object_layer)->object()->config, true);
+                Point copy = (layer_to_print.object_layer)->object()->_shifted_copies.front();
+                this->set_origin(unscale(copy.x), unscale(copy.y));
+                gcode += this->extrude_infill(print, overridden);
+            }
+        }
+
+
         auto objects_by_extruder_it = by_extruder.find(extruder_id);
         if (objects_by_extruder_it == by_extruder.end())
             continue;
         for (const ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
             const size_t       layer_id     = &object_by_extruder - objects_by_extruder_it->second.data();
             const PrintObject *print_object = layers[layer_id].object();
-			if (print_object == nullptr)
-				// This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
-				continue;
+            if (print_object == nullptr)
+                // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
+                continue;
 
             m_config.apply(print_object->config, true);
             m_layer = layers[layer_id].layer();
@@ -1355,6 +1381,7 @@ void GCode::process_layer(
                 copies.push_back(print_object->_shifted_copies[single_object_idx]);
             // Sort the copies by the closest point starting with the current print position.
             
+
             for (const Point &copy : copies) {
                 // When starting a new object, use the external motion planner for the first travel move.
                 std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
@@ -2004,6 +2031,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
 }
 
 // Chain the paths hierarchically by a greedy algorithm to minimize a travel distance.
+// if extruder_id is set, only entities marked with given extruder_id are extruded
 std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region)
 {
     std::string gcode;
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 643ab3f31..82f513d70 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1037,6 +1037,26 @@ void Print::_make_wipe_tower()
     if (! this->has_wipe_tower())
         return;
 
+
+    int wiping_extruder = 0;
+
+    for (size_t i = 0; i < objects.size(); ++ i) {
+        for (Layer* lay : objects[i]->layers) {
+            for (LayerRegion* reg : lay->regions) {
+                ExtrusionEntityCollection& eec = reg->fills;
+                for (ExtrusionEntity* ee : eec.entities) {
+                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                        /*if (fill->total_volume() > 1.)*/ {
+                            fill->set_extruder_override(wiping_extruder);
+                        if (++wiping_extruder > 3)
+                            wiping_extruder = 0;
+                        }
+                }
+            }
+        }
+    }
+
+
     // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
     m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
     if (! m_tool_ordering.has_wipe_tower())

From 132a67edb21ca0bb5deb77e32de4af0cf92da3a0 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 24 May 2018 17:24:37 +0200
Subject: [PATCH 08/36] Wipe tower changes to reduce wiping volumes where
 appropriate

---
 xs/src/libslic3r/GCode/ToolOrdering.cpp     |   2 +
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp |  11 +-
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  22 ++--
 xs/src/libslic3r/Print.cpp                  | 114 +++++++-------------
 4 files changed, 57 insertions(+), 92 deletions(-)

diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 271b75ef3..671dadc5a 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -217,6 +217,8 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
         }
 }
 
+
+
 void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
 {
     if (m_layer_tools.empty())
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 731dcbbd9..46fa0fc6d 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -557,7 +557,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	{
 		for (const auto &b : m_layer_info->tool_changes)
 			if ( b.new_tool == tool ) {
-				wipe_volume = wipe_volumes[b.old_tool][b.new_tool];
+				wipe_volume = b.wipe_volume;
 				if (tool == m_layer_info->tool_changes.back().new_tool)
 					last_change_in_layer = true;
 				wipe_area = b.required_depth * m_layer_info->extra_spacing;
@@ -1051,7 +1051,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 }
 
 // Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
-void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool,bool brim)
+void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wiping_volume_reduction)
 {
 	assert(m_plan.back().z <= z_par + WT_EPSILON );	// refuses to add a layer below the last one
 
@@ -1076,13 +1076,14 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
     float ramming_depth = depth;
     length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
     float first_wipe_line = -length_to_extrude;
-    length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_perimeter_width, layer_height_par);
+    float wipe_volume = wipe_volumes[old_tool][new_tool] - wiping_volume_reduction;
+    length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
     length_to_extrude = std::max(length_to_extrude,0.f);
 
 	depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
 	depth *= m_extra_spacing;
 
-	m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth,first_wipe_line));
+	m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth, first_wipe_line, wipe_volume));
 }
 
 
@@ -1122,7 +1123,7 @@ void WipeTowerPrusaMM::save_on_last_wipe()
 
         float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
         float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
-        float length_to_wipe = volume_to_length(wipe_volumes[m_layer_info->tool_changes.back().old_tool][m_layer_info->tool_changes.back().new_tool],
+        float length_to_wipe = volume_to_length(m_layer_info->tool_changes.back().wipe_volume,
                               m_perimeter_width,m_layer_info->height)  - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
 
         length_to_wipe = std::max(length_to_wipe,0.f);
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index ea1c1f631..04ae81e6d 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -44,7 +44,7 @@ public:
 	// 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,
-                     const std::vector<float>& wiping_matrix, unsigned int initial_tool) :
+                     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),
@@ -56,12 +56,9 @@ public:
         m_parking_pos_retraction(parking_pos_retraction),
         m_extra_loading_move(extra_loading_move),
 		m_bridging(bridging),
-        m_current_tool(initial_tool)
- 	{
-        unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+WT_EPSILON);
-        for (unsigned int i = 0; i<number_of_extruders; ++i)
-            wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders,wiping_matrix.begin()+(i+1)*number_of_extruders));
-	}
+        m_current_tool(initial_tool),
+        wipe_volumes(wiping_matrix)
+        {}
 
 	virtual ~WipeTowerPrusaMM() {}
 
@@ -99,7 +96,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);
+	void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wiping_volume_reduction = 0.f);
 
 	// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
 	void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
@@ -238,7 +235,7 @@ private:
 	// 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;
-    std::vector<std::vector<float>> wipe_volumes;
+    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;
@@ -255,7 +252,7 @@ private:
 
 	// Calculates length of extrusion line to extrude given volume
 	float volume_to_length(float volume, float line_width, float layer_height) const {
-		return volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.)));
+		return std::max(0., volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.))));
 	}
 
 	// Calculates depth for all layers and propagates them downwards
@@ -308,8 +305,9 @@ private:
 			float required_depth;
             float ramming_depth;
             float first_wipe_line;
-			ToolChange(unsigned int old,unsigned int newtool,float depth=0.f,float ramming_depth=0.f,float fwl=0.f)
-            : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth},first_wipe_line{fwl} {}
+            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
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 82f513d70..64bfb45ca 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1037,25 +1037,13 @@ void Print::_make_wipe_tower()
     if (! this->has_wipe_tower())
         return;
 
-
-    int wiping_extruder = 0;
-
-    for (size_t i = 0; i < objects.size(); ++ i) {
-        for (Layer* lay : objects[i]->layers) {
-            for (LayerRegion* reg : lay->regions) {
-                ExtrusionEntityCollection& eec = reg->fills;
-                for (ExtrusionEntity* ee : eec.entities) {
-                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                        /*if (fill->total_volume() > 1.)*/ {
-                            fill->set_extruder_override(wiping_extruder);
-                        if (++wiping_extruder > 3)
-                            wiping_extruder = 0;
-                        }
-                }
-            }
-        }
-    }
-
+    // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
+    std::vector<float> wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
+    // Extract purging volumes for each extruder pair:
+    std::vector<std::vector<float>> wipe_volumes;
+    const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
+    for (unsigned int i = 0; i<number_of_extruders; ++i)
+        wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
 
     // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
     m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
@@ -1101,23 +1089,20 @@ void Print::_make_wipe_tower()
         }
     }
 
-    // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
-    std::vector<float> wiping_volumes((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
-
     // Initialize the wipe tower.
     WipeTowerPrusaMM wipe_tower(
         float(this->config.wipe_tower_x.value),     float(this->config.wipe_tower_y.value), 
         float(this->config.wipe_tower_width.value),
         float(this->config.wipe_tower_rotation_angle.value), float(this->config.cooling_tube_retraction.value),
         float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value),
-        float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wiping_volumes,
+        float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wipe_volumes,
         m_tool_ordering.first_extruder());
 
     //wipe_tower.set_retract();
     //wipe_tower.set_zhop();
 
     // Set the extruder & material properties at the wipe tower object.
-    for (size_t i = 0; i < (int)(sqrt(wiping_volumes.size())+EPSILON); ++ i)
+    for (size_t i = 0; i < number_of_extruders; ++ i)
         wipe_tower.set_extruder(
             i, 
             WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
@@ -1151,7 +1136,36 @@ void Print::_make_wipe_tower()
             wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
             for (const auto extruder_id : layer_tools.extruders) {
                 if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
-                    wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back());
+
+                    // Toolchange from old_extruder to new_extruder.
+                    // Check how much volume needs to be wiped and keep marking infills until
+                    // we run out of the volume (or infills)
+                    const float min_infill_volume = 0.f;
+
+                    if (config.filament_soluble.get_at(extruder_id)) // soluble filament cannot be wiped in a random infill
+                        continue;
+
+                    float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];
+
+                    for (size_t i = 0; i < objects.size(); ++ i) {                    // Let's iterate through all objects...
+                        for (Layer* lay : objects[i]->layers) {
+                            for (LayerRegion* reg : lay->regions) {                         // and all regions
+                                ExtrusionEntityCollection& eec = reg->fills;
+                                for (ExtrusionEntity* ee : eec.entities) {                  // and all infill Collections
+                                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                                        if (volume_to_wipe > 0.f && !fill->is_extruder_overridden() && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                                            fill->set_extruder_override(extruder_id);
+                                            volume_to_wipe -= fill->total_volume();
+                                        }
+                                }
+                            }
+                        }
+                    }
+
+                    float saved_material = wipe_volumes[current_extruder_id][extruder_id] - std::max(0.f, volume_to_wipe);
+                    std::cout << volume_to_wipe << "\t(saved " << saved_material << ")" << std::endl;
+
+                    wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), saved_material);
                     current_extruder_id = extruder_id;
                 }
             }
@@ -1160,60 +1174,10 @@ void Print::_make_wipe_tower()
         }
     }
 
-    
-
     // Generate the wipe tower layers.
     m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
     wipe_tower.generate(m_wipe_tower_tool_changes);
     
-    // Set current_extruder_id to the last extruder primed.
-    /*unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
-
-    for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) {
-        if (! layer_tools.has_wipe_tower)
-            // This is a support only layer, or the wipe tower does not reach to this height.
-            continue;
-        bool first_layer = &layer_tools == &m_tool_ordering.front();
-        bool last_layer  = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0;
-        wipe_tower.set_layer(
-            float(layer_tools.print_z), 
-            float(layer_tools.wipe_tower_layer_height),
-            layer_tools.wipe_tower_partitions,
-            first_layer,
-            last_layer);
-        std::vector<WipeTower::ToolChangeResult> tool_changes;
-        for (unsigned int extruder_id : layer_tools.extruders)
-            // Call the wipe_tower.tool_change() at the first layer for the initial extruder 
-            // to extrude the wipe tower brim,
-            if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || 
-            // or when an extruder shall be switched.
-                extruder_id != current_extruder_id) {
-                tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, extruder_id == layer_tools.extruders.back(), WipeTower::PURPOSE_EXTRUDE));
-                current_extruder_id = extruder_id;
-            }
-        if (! wipe_tower.layer_finished()) {
-            tool_changes.emplace_back(wipe_tower.finish_layer(WipeTower::PURPOSE_EXTRUDE));
-            if (tool_changes.size() > 1) {
-                // Merge the two last tool changes into one.
-                WipeTower::ToolChangeResult &tc1 = tool_changes[tool_changes.size() - 2];
-                WipeTower::ToolChangeResult &tc2 = tool_changes.back();
-                if (tc1.end_pos != tc2.start_pos) {
-                    // Add a travel move from tc1.end_pos to tc2.start_pos.
-                    char buf[2048];
-                    sprintf(buf, "G1 X%.3f Y%.3f F7200\n", tc2.start_pos.x, tc2.start_pos.y);
-                    tc1.gcode += buf;
-                }
-                tc1.gcode += tc2.gcode;
-                append(tc1.extrusions, tc2.extrusions);
-                tc1.end_pos = tc2.end_pos;
-                tool_changes.pop_back();
-            }
-        }
-        m_wipe_tower_tool_changes.emplace_back(std::move(tool_changes));
-        if (last_layer)
-            break;
-    }*/
-    
     // Unload the current filament over the purge tower.
     coordf_t layer_height = this->objects.front()->config.layer_height.value;
     if (m_tool_ordering.back().wipe_tower_partitions > 0) {

From bfe4350a89bea904c39e640b8779542a0a9642d1 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 25 May 2018 16:11:55 +0200
Subject: [PATCH 09/36] Calculation of wipe tower reduction corrected, new
 config option (wipe into infill)

---
 xs/src/libslic3r/GCode.cpp       | 41 ++++++++++++++++++--------------
 xs/src/libslic3r/Print.cpp       | 33 +++++++++++++------------
 xs/src/libslic3r/PrintConfig.cpp | 10 +++++++-
 xs/src/libslic3r/PrintConfig.hpp |  2 ++
 xs/src/slic3r/GUI/Preset.cpp     |  3 ++-
 xs/src/slic3r/GUI/Tab.cpp        |  3 ++-
 6 files changed, 56 insertions(+), 36 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 3536c0c9c..0ccc4384e 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1338,26 +1338,31 @@ void GCode::process_layer(
             m_avoid_crossing_perimeters.disable_once = true;
         }
 
-        for (const auto& layer_to_print : layers) {   // iterate through all objects
-            if (layer_to_print.object_layer == nullptr)
-                continue;
-            std::vector<ObjectByExtruder::Island::Region> overridden;
-            for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
-                ObjectByExtruder::Island::Region new_region;
-                overridden.push_back(new_region);
-                for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
-                    auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                    if (fill->get_extruder_override() == extruder_id) {
-                        overridden.back().infills.append(*fill);
-                        fill->set_extruder_override(-1);
-                    }
-               }
-                m_config.apply((layer_to_print.object_layer)->object()->config, true);
-                Point copy = (layer_to_print.object_layer)->object()->_shifted_copies.front();
-                this->set_origin(unscale(copy.x), unscale(copy.y));
-                gcode += this->extrude_infill(print, overridden);
+        gcode += "; INFILL WIPING STARTS\n";
+
+        if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange
+            for (const auto& layer_to_print : layers) {     // iterate through all objects
+                if (layer_to_print.object_layer == nullptr)
+                    continue;
+                std::vector<ObjectByExtruder::Island::Region> overridden;
+                for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
+                    ObjectByExtruder::Island::Region new_region;
+                    overridden.push_back(new_region);
+                    for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
+                        auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                        if (fill->get_extruder_override() == extruder_id) {
+                            overridden.back().infills.append(*fill);
+                            fill->set_extruder_override(-1);
+                        }
+                   }
+                    m_config.apply((layer_to_print.object_layer)->object()->config, true);
+                    Point copy = (layer_to_print.object_layer)->object()->_shifted_copies.front();
+                    this->set_origin(unscale(copy.x), unscale(copy.y));
+                    gcode += this->extrude_infill(print, overridden);
+                }
             }
         }
+        gcode += "; WIPING FINISHED\n";
 
 
         auto objects_by_extruder_it = by_extruder.find(extruder_id);
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 64bfb45ca..e09cafa56 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1142,28 +1142,31 @@ void Print::_make_wipe_tower()
                     // we run out of the volume (or infills)
                     const float min_infill_volume = 0.f;
 
-                    if (config.filament_soluble.get_at(extruder_id)) // soluble filament cannot be wiped in a random infill
-                        continue;
-
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];
+                    float saved_material = 0.f;
 
-                    for (size_t i = 0; i < objects.size(); ++ i) {                    // Let's iterate through all objects...
-                        for (Layer* lay : objects[i]->layers) {
-                            for (LayerRegion* reg : lay->regions) {                         // and all regions
-                                ExtrusionEntityCollection& eec = reg->fills;
-                                for (ExtrusionEntity* ee : eec.entities) {                  // and all infill Collections
-                                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                        if (volume_to_wipe > 0.f && !fill->is_extruder_overridden() && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
-                                            fill->set_extruder_override(extruder_id);
-                                            volume_to_wipe -= fill->total_volume();
-                                        }
+                    // soluble filament cannot be wiped in a random infill, first layer is potentionally visible too
+                    if (!first_layer && !config.filament_soluble.get_at(extruder_id)) {
+                        for (size_t i = 0; i < objects.size(); ++ i) {                    // Let's iterate through all objects...
+                            for (Layer* lay : objects[i]->layers) {
+                                if (std::abs(layer_tools.print_z - lay->print_z) > EPSILON) continue;
+                                for (LayerRegion* reg : lay->regions) {                         // and all regions
+                                    ExtrusionEntityCollection& eec = reg->fills;
+                                    for (ExtrusionEntity* ee : eec.entities) {                  // and all infill Collections
+                                            auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                                            if (fill->role() == erTopSolidInfill) continue;
+                                            if (volume_to_wipe > 0.f && !fill->is_extruder_overridden() && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                                                fill->set_extruder_override(extruder_id);
+                                                volume_to_wipe -= fill->total_volume();
+                                            }
+                                    }
                                 }
                             }
                         }
                     }
 
-                    float saved_material = wipe_volumes[current_extruder_id][extruder_id] - std::max(0.f, volume_to_wipe);
-                    std::cout << volume_to_wipe << "\t(saved " << saved_material << ")" << std::endl;
+                    saved_material = wipe_volumes[current_extruder_id][extruder_id] - std::max(0.f, volume_to_wipe);
+                    std::cout << layer_tools.print_z << "\t" << extruder_id << "\t" << wipe_volumes[current_extruder_id][extruder_id] - volume_to_wipe << "\n";
 
                     wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), saved_material);
                     current_extruder_id = extruder_id;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index e3cbf5243..bf9421f9d 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1884,7 +1884,15 @@ PrintConfigDef::PrintConfigDef()
     def->sidetext = L("degrees");
     def->cli = "wipe-tower-rotation-angle=f";
     def->default_value = new ConfigOptionFloat(0.);
-    
+
+    def = this->add("wipe_into_infill", coBool);
+    def->label = L("Wiping into infill");
+    def->tooltip = L("Wiping after toolchange will be preferentially done inside infills. "
+                     "This lowers the amount of waste but may result in longer print time "
+                     " due to additional travel moves.");
+    def->cli = "wipe-into-infill!";
+    def->default_value = new ConfigOptionBool(true);
+
     def = this->add("wipe_tower_bridging", coFloat);
     def->label = L("Maximal bridging distance");
     def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index a36e5def9..1b73c31b3 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -642,6 +642,7 @@ public:
     ConfigOptionFloat               wipe_tower_per_color_wipe;
     ConfigOptionFloat               wipe_tower_rotation_angle;
     ConfigOptionFloat               wipe_tower_bridging;
+    ConfigOptionBool                wipe_into_infill;
     ConfigOptionFloats              wiping_volumes_matrix;
     ConfigOptionFloats              wiping_volumes_extruders;
     ConfigOptionFloat               z_offset;
@@ -710,6 +711,7 @@ protected:
         OPT_PTR(wipe_tower_width);
         OPT_PTR(wipe_tower_per_color_wipe);
         OPT_PTR(wipe_tower_rotation_angle);
+        OPT_PTR(wipe_into_infill);
         OPT_PTR(wipe_tower_bridging);
         OPT_PTR(wiping_volumes_matrix);
         OPT_PTR(wiping_volumes_extruders);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index ce6918406..e483381ac 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -298,7 +298,8 @@ const std::vector<std::string>& Preset::print_options()
         "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", 
         "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", 
         "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
-        "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers", "compatible_printers_condition","inherits"
+        "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "wipe_into_infill", "compatible_printers",
+        "compatible_printers_condition","inherits"
     };
     return s_opts;
 }
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index b3e737f6e..c94307aa4 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -945,6 +945,7 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_width");
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
         optgroup->append_single_option_line("wipe_tower_bridging");
+        optgroup->append_single_option_line("wipe_into_infill");
 
 		optgroup = page->new_optgroup(_(L("Advanced")));
 		optgroup->append_single_option_line("interface_shells");
@@ -1233,7 +1234,7 @@ void TabPrint::update()
 	get_field("standby_temperature_delta")->toggle(have_ooze_prevention);
 
 	bool have_wipe_tower = m_config->opt_bool("wipe_tower");
-	for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"})
+	for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_into_infill", "wipe_tower_bridging"})
 		get_field(el)->toggle(have_wipe_tower);
 
 	m_recommended_thin_wall_thickness_description_line->SetText(

From c72ecb382d2308588a8ddc46c5a68982df47f371 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 28 May 2018 15:33:19 +0200
Subject: [PATCH 10/36] Reduction is now correctly calculated for each region,
 soluble filament excluded from infill wiping

---
 xs/src/libslic3r/GCode.cpp | 42 ++++++++++++++++++++++----------------
 xs/src/libslic3r/Print.cpp | 22 ++++++++++++--------
 2 files changed, 37 insertions(+), 27 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 0ccc4384e..1ce181517 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1338,31 +1338,37 @@ void GCode::process_layer(
             m_avoid_crossing_perimeters.disable_once = true;
         }
 
-        gcode += "; INFILL WIPING STARTS\n";
-
-        if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange
-            for (const auto& layer_to_print : layers) {     // iterate through all objects
-                if (layer_to_print.object_layer == nullptr)
-                    continue;
-                std::vector<ObjectByExtruder::Island::Region> overridden;
-                for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
-                    ObjectByExtruder::Island::Region new_region;
-                    overridden.push_back(new_region);
-                    for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
-                        auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                        if (fill->get_extruder_override() == extruder_id) {
-                            overridden.back().infills.append(*fill);
-                            fill->set_extruder_override(-1);
-                        }
-                   }
+        if (print.config.wipe_into_infill.value) {
+            gcode += "; INFILL WIPING STARTS\n";
+            if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange
+                for (const auto& layer_to_print : layers) {     // iterate through all objects
+                gcode+="objekt\n";
+                    if (layer_to_print.object_layer == nullptr)
+                        continue;
+                    std::vector<ObjectByExtruder::Island::Region> overridden;
+                    for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
+                        gcode+="region\n";
+                        ObjectByExtruder::Island::Region new_region;
+                        overridden.push_back(new_region);
+                        for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
+                            gcode+="entity\n";
+                            auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                            if (fill->get_extruder_override() == extruder_id) {
+                                gcode+="*\n";
+                                overridden.back().infills.append(*fill);
+                                fill->set_extruder_override(-1);
+                            }
+                       }
+                    }
                     m_config.apply((layer_to_print.object_layer)->object()->config, true);
                     Point copy = (layer_to_print.object_layer)->object()->_shifted_copies.front();
                     this->set_origin(unscale(copy.x), unscale(copy.y));
                     gcode += this->extrude_infill(print, overridden);
                 }
             }
+            gcode += "; WIPING FINISHED\n";
         }
-        gcode += "; WIPING FINISHED\n";
+
 
 
         auto objects_by_extruder_it = by_extruder.find(extruder_id);
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index e09cafa56..92c2715fb 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1145,16 +1145,20 @@ void Print::_make_wipe_tower()
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];
                     float saved_material = 0.f;
 
-                    // soluble filament cannot be wiped in a random infill, first layer is potentionally visible too
-                    if (!first_layer && !config.filament_soluble.get_at(extruder_id)) {
-                        for (size_t i = 0; i < objects.size(); ++ i) {                    // Let's iterate through all objects...
-                            for (Layer* lay : objects[i]->layers) {
-                                if (std::abs(layer_tools.print_z - lay->print_z) > EPSILON) continue;
-                                for (LayerRegion* reg : lay->regions) {                         // and all regions
-                                    ExtrusionEntityCollection& eec = reg->fills;
-                                    for (ExtrusionEntity* ee : eec.entities) {                  // and all infill Collections
+
+                    if (!first_layer && !config.filament_soluble.get_at(extruder_id)) {             // soluble filament cannot be wiped in a random infill, first layer is potentionally visible too
+                        for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
+                            for (Layer* lay : objects[i]->layers) {                                 // Find this layer
+                                if (std::abs(layer_tools.print_z - lay->print_z) > EPSILON)
+                                    continue;
+                                for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
+                                    unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
+                                    if (config.filament_soluble.get_at(region_extruder)) // if this infill is meant to be soluble, keep it that way
+                                        continue;
+                                    ExtrusionEntityCollection& eec = lay->regions[region_id]->fills;
+                                    for (ExtrusionEntity* ee : eec.entities) {                      // and all infill Collections
                                             auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                            if (fill->role() == erTopSolidInfill) continue;
+                                            if (fill->role() == erTopSolidInfill) continue;         // color of TopSolidInfill cannot be changed - it is visible
                                             if (volume_to_wipe > 0.f && !fill->is_extruder_overridden() && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
                                                 fill->set_extruder_override(extruder_id);
                                                 volume_to_wipe -= fill->total_volume();

From 549351bbb4e17685c99af9d07b6555d0bf8fad55 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 29 May 2018 12:32:04 +0200
Subject: [PATCH 11/36] Analyzer tags for the wipe tower also generate layer
 height and line width (so the priming lines+brim are visible and ramming
 lines are correct width)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 55 ++++++++++++---------
 1 file changed, 31 insertions(+), 24 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 46fa0fc6d..9695cc7a8 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -42,14 +42,31 @@ namespace PrusaMultiMaterial {
 class Writer
 {
 public:
-	Writer() : 
+	Writer(float layer_height, float line_width) :
 		m_current_pos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
 		m_current_z(0.f),
 		m_current_feedrate(0.f),
-		m_layer_height(0.f),
+		m_layer_height(layer_height),
 		m_extrusion_flow(0.f),
 		m_preview_suppressed(false),
-		m_elapsed_time(0.f) {}
+		m_elapsed_time(0.f),
+        m_default_analyzer_line_width(line_width)
+        {
+            // adds tag for analyzer:
+            char buf[64];
+            sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
+            m_gcode += buf;
+            sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
+            m_gcode += buf;
+            change_analyzer_line_width(line_width);
+        }
+
+    Writer&              change_analyzer_line_width(float line_width) {
+            // adds tag for analyzer:
+            char buf[64];
+            sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
+            m_gcode += buf;
+    }
 
 	Writer& 			 set_initial_position(const WipeTower::xy &pos) { 
 		m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg);
@@ -62,9 +79,6 @@ public:
 	Writer&				 set_z(float z) 
 		{ m_current_z = z; return *this; }
 
-	Writer&				 set_layer_height(float layer_height)
-		{ m_layer_height = layer_height; return *this; }
-
 	Writer& 			 set_extrusion_flow(float flow)
 		{ m_extrusion_flow = flow; return *this; }
 		
@@ -80,8 +94,8 @@ public:
 	// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
 	// filament loading and cooling moves from normal extrusion moves. Therefore the writer
 	// is asked to suppres output of some lines, which look like extrusions.
-	Writer& 			 suppress_preview() { m_preview_suppressed = true; return *this; }
-	Writer& 			 resume_preview() { m_preview_suppressed = false; return *this; }
+	Writer& 			 suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
+	Writer& 			 resume_preview()   { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
 
 	Writer& 			 feedrate(float f)
 	{
@@ -126,11 +140,6 @@ public:
 			m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));			
 		}
 
-        // adds tag for analyzer
-        char buf[64];
-        sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
-        m_gcode += buf;
-
 		m_gcode += "G1";
 		if (rot.x != rotated_current_pos.x) {
 			m_gcode += set_format_X(rot.x);     // Transform current position back to wipe tower coordinates (was updated by set_format_X)
@@ -397,6 +406,7 @@ private:
 	float 		  m_wipe_tower_width = 0.f;
 	float		  m_wipe_tower_depth = 0.f;
 	float		  m_last_fan_speed = 0.f;
+    const float   m_default_analyzer_line_width;
 
 	std::string   set_format_X(float x)
 	{
@@ -485,10 +495,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	const float prime_section_width = std::min(240.f / tools.size(), 60.f);
 	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 100.f);
 
-	PrusaMultiMaterial::Writer writer;
+	PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
 	writer.set_extrusion_flow(m_extrusion_flow)
 		  .set_z(m_z_pos)
-		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
 		  .append(";--------------------\n"
 			 	  "; CP PRIMING START\n")
@@ -574,10 +583,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
                                     : m_wipe_tower_depth-m_perimeter_width));
 
-	PrusaMultiMaterial::Writer writer;
+	PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
 	writer.set_extrusion_flow(m_extrusion_flow)
 		.set_z(m_z_pos)
-		.set_layer_height(m_layer_height)
 		.set_initial_tool(m_current_tool)
 		.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
 		.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
@@ -646,10 +654,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
 		m_wipe_tower_width,
 		m_wipe_tower_depth);
 
-	PrusaMultiMaterial::Writer writer;
+	PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
 	writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
 		  .set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
-		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
   		  .set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
 		  .append(";-------------------------------------\n"
@@ -703,11 +710,12 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	float xl = cleaning_box.ld.x + 1.f * m_perimeter_width;
 	float xr = cleaning_box.rd.x - 1.f * m_perimeter_width;
 	
-	writer.append("; CP TOOLCHANGE UNLOAD\n");
-	
 	const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator;       // desired ramming line thickness
 	const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
 
+    writer.append("; CP TOOLCHANGE UNLOAD\n")
+          .change_analyzer_line_width(line_width);
+
 	unsigned i = 0;										// iterates through ramming_speed
 	m_left_to_right = true;								// current direction of ramming
 	float remaining = xr - xl ;							// keeps track of distance to the next turnaround
@@ -781,7 +789,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 		}
 	}
 	WipeTower::xy end_of_ramming(writer.x(),writer.y());
-
+    writer.change_analyzer_line_width(m_perimeter_width);   // so the next lines are not affected by ramming_line_width_multiplier
 
     // Retraction:
     float old_x = writer.x();
@@ -960,10 +968,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 	// Otherwise the caller would likely travel to the wipe tower in vain.
 	assert(! this->layer_finished());
 
-	PrusaMultiMaterial::Writer writer;
+	PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
 	writer.set_extrusion_flow(m_extrusion_flow)
 		.set_z(m_z_pos)
-		.set_layer_height(m_layer_height)
 		.set_initial_tool(m_current_tool)
 		.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
 		.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))

From 8bdbe4150574f9607be28a8005c94448ec951dd1 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 30 May 2018 11:56:30 +0200
Subject: [PATCH 12/36] Wiping into infill should respect infill_first setting,
 marking moved to separate function

---
 xs/src/libslic3r/GCode/ToolOrdering.cpp     |  1 +
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 14 ++--
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  2 +-
 xs/src/libslic3r/Print.cpp                  | 85 ++++++++++++---------
 xs/src/libslic3r/Print.hpp                  |  7 +-
 5 files changed, 64 insertions(+), 45 deletions(-)

diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 671dadc5a..e0aa2b1c5 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -76,6 +76,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
     this->collect_extruder_statistics(prime_multi_material);
 }
 
+
 ToolOrdering::LayerTools&  ToolOrdering::tools_for_layer(coordf_t print_z)
 {
     auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), ToolOrdering::LayerTools(print_z - EPSILON));
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 9695cc7a8..45d28e839 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -66,6 +66,7 @@ public:
             char buf[64];
             sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
             m_gcode += buf;
+            return *this;
     }
 
 	Writer& 			 set_initial_position(const WipeTower::xy &pos) { 
@@ -137,7 +138,7 @@ public:
 			width += m_layer_height * float(1. - M_PI / 4.);
 			if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
 				m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
-			m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));			
+			m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
 		}
 
 		m_gcode += "G1";
@@ -483,7 +484,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
 	bool 						last_wipe_inside_wipe_tower)
 {
-
 	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
 	this->m_current_tool 		= tools.front();
     
@@ -1058,7 +1058,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 }
 
 // Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
-void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wiping_volume_reduction)
+void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
 {
 	assert(m_plan.back().z <= z_par + WT_EPSILON );	// refuses to add a layer below the last one
 
@@ -1083,7 +1083,6 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
     float ramming_depth = depth;
     length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
     float first_wipe_line = -length_to_extrude;
-    float wipe_volume = wipe_volumes[old_tool][new_tool] - wiping_volume_reduction;
     length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
     length_to_extrude = std::max(length_to_extrude,0.f);
 
@@ -1146,7 +1145,8 @@ void WipeTowerPrusaMM::save_on_last_wipe()
 // Resulting ToolChangeResults are appended into vector "result"
 void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
 {
-	if (m_plan.empty())	return;
+	if (m_plan.empty())
+        return;
 
     m_extra_spacing = 1.f;
 
@@ -1161,12 +1161,10 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 
     m_layer_info = m_plan.begin();
 
-	std::vector<WipeTower::ToolChangeResult> layer_result;
+    std::vector<WipeTower::ToolChangeResult> layer_result;
 	for (auto layer : m_plan)
 	{
 		set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
-
-
 		if (m_peters_wipe_tower)
 			m_wipe_tower_rotation_angle += 90.f;
 		else
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 04ae81e6d..54cb51658 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -96,7 +96,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 wiping_volume_reduction = 0.f);
+	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);
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 92c2715fb..7e5ac0812 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1136,43 +1136,12 @@ void Print::_make_wipe_tower()
             wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
             for (const auto extruder_id : layer_tools.extruders) {
                 if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
+                    float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];    // total volume to wipe after this toolchange
 
-                    // Toolchange from old_extruder to new_extruder.
-                    // Check how much volume needs to be wiped and keep marking infills until
-                    // we run out of the volume (or infills)
-                    const float min_infill_volume = 0.f;
+                    if (config.wipe_into_infill && !first_layer)
+                        volume_to_wipe = mark_wiping_infill(layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
 
-                    float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];
-                    float saved_material = 0.f;
-
-
-                    if (!first_layer && !config.filament_soluble.get_at(extruder_id)) {             // soluble filament cannot be wiped in a random infill, first layer is potentionally visible too
-                        for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
-                            for (Layer* lay : objects[i]->layers) {                                 // Find this layer
-                                if (std::abs(layer_tools.print_z - lay->print_z) > EPSILON)
-                                    continue;
-                                for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
-                                    unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
-                                    if (config.filament_soluble.get_at(region_extruder)) // if this infill is meant to be soluble, keep it that way
-                                        continue;
-                                    ExtrusionEntityCollection& eec = lay->regions[region_id]->fills;
-                                    for (ExtrusionEntity* ee : eec.entities) {                      // and all infill Collections
-                                            auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                            if (fill->role() == erTopSolidInfill) continue;         // color of TopSolidInfill cannot be changed - it is visible
-                                            if (volume_to_wipe > 0.f && !fill->is_extruder_overridden() && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
-                                                fill->set_extruder_override(extruder_id);
-                                                volume_to_wipe -= fill->total_volume();
-                                            }
-                                    }
-                                }
-                            }
-                        }
-                    }
-
-                    saved_material = wipe_volumes[current_extruder_id][extruder_id] - std::max(0.f, volume_to_wipe);
-                    std::cout << layer_tools.print_z << "\t" << extruder_id << "\t" << wipe_volumes[current_extruder_id][extruder_id] - volume_to_wipe << "\n";
-
-                    wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), saved_material);
+                    wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
                     current_extruder_id = extruder_id;
                 }
             }
@@ -1205,6 +1174,52 @@ void Print::_make_wipe_tower()
 		wipe_tower.tool_change((unsigned int)-1, false));
 }
 
+
+
+float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
+{
+    const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
+
+    if (!config.filament_soluble.get_at(new_extruder)) {                            // Soluble filament cannot be wiped in a random infill
+        for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
+            Layer* this_layer = nullptr;
+            for (unsigned int a = 0; a < objects[i]->layers.size(); this_layer = objects[i]->layers[++a]) // Finds this layer
+                if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON)
+                    break;
+
+            for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
+                unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
+                if (config.filament_soluble.get_at(region_extruder)) // if this infill is meant to be soluble, keep it that way
+                    continue;
+
+                if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded)
+                    bool unused_yet = false;
+                    for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
+                        if (layer_tools.extruders[i] == new_extruder)
+                            unused_yet = true;
+                        if (layer_tools.extruders[i] == region_extruder)
+                            break;
+                    }
+                    if (unused_yet)
+                        continue;
+                }
+
+                ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
+                for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
+                    auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                    if (fill->role() == erTopSolidInfill) continue;         // color of TopSolidInfill cannot be changed - it is visible
+                    if (volume_to_wipe > 0.f && !fill->is_extruder_overridden() && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                        fill->set_extruder_override(new_extruder);
+                        volume_to_wipe -= fill->total_volume();
+                    }
+                }
+            }
+        }
+    }
+    return std::max(0.f, volume_to_wipe);
+}
+
+
 std::string Print::output_filename()
 {
     this->placeholder_parser.update_timestamp();
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index c56e64c6c..77b47fb83 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -309,11 +309,16 @@ public:
     void restart() { m_canceled = false; }
     // Has the calculation been canceled?
     bool canceled() { return m_canceled; }
-    
+
+
 private:
     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
     PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
 
+    // 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_infill(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
+
     // Has the calculation been canceled?
     tbb::atomic<bool>   m_canceled;
 };

From 2d24bf5f73ac722cc83bc8f279631572d6ed6426 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 31 May 2018 16:21:10 +0200
Subject: [PATCH 13/36] Wipe into infill - copies of one object are properly
 processed

---
 xs/src/libslic3r/ExtrusionEntity.hpp          | 13 ++++
 .../libslic3r/ExtrusionEntityCollection.hpp   | 18 ++----
 xs/src/libslic3r/GCode.cpp                    | 63 ++++++++++++-------
 xs/src/libslic3r/GCode.hpp                    |  3 +
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp   |  1 -
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp   |  1 +
 xs/src/libslic3r/Print.cpp                    | 56 +++++++++--------
 7 files changed, 92 insertions(+), 63 deletions(-)

diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 15363e8ed..c0f681de5 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -93,6 +93,19 @@ public:
     virtual Polyline as_polyline() const = 0;
     virtual double length() const = 0;
     virtual double total_volume() const = 0;
+
+    void set_entity_extruder_override(unsigned int copy, int extruder) {
+        if (copy+1 > extruder_override.size())
+            extruder_override.resize(copy+1, -1); // copy is zero-based index
+        extruder_override[copy] = extruder;
+    }
+    virtual int get_extruder_override(unsigned int copy) const   { try { return extruder_override.at(copy);       } catch (...) { return -1;    } }
+    virtual bool is_extruder_overridden(unsigned int copy) const { try { return extruder_override.at(copy) != -1; } catch (...) { return false; } }
+
+private:
+    // Set this variable to explicitly state you want to use specific extruder for thie EE (used for MM infill wiping)
+    // Each member of the vector corresponds to the respective copy of the object
+    std::vector<int> extruder_override;
 };
 
 typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index d292248fc..ee4b75f38 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -91,20 +91,12 @@ public:
         return 0.;        
     }
 
-    void set_extruder_override(int extruder) {
-        extruder_override = extruder;
-        for (auto& member : entities) {
-            if (member->is_collection())
-                dynamic_cast<ExtrusionEntityCollection*>(member)->set_extruder_override(extruder);
-        }
+    void set_extruder_override(unsigned int copy, int extruder) {
+        for (ExtrusionEntity* member : entities)
+            member->set_entity_extruder_override(copy, extruder);
     }
-    int get_extruder_override() const { return extruder_override; }
-    bool is_extruder_overridden() const { return extruder_override != -1; }
-
-
-private:
-    // Set this variable to explicitly state you want to use specific extruder for thie EEC (used for MM infill wiping)
-    int extruder_override = -1;
+    virtual int get_extruder_override(unsigned int copy) const   { return entities.front()->get_extruder_override(copy);  }
+    virtual bool is_extruder_overridden(unsigned int copy) const { return entities.front()->is_extruder_overridden(copy); }
 };
 
 }
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 1ce181517..bbca523e3 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1262,8 +1262,8 @@ void GCode::process_layer(
                         // This shouldn't happen but first_point() would fail.
                         continue;
 
-                    if (fill->is_extruder_overridden())
-                        continue;
+                    /*if (fill->is_extruder_overridden())
+                        continue;*/
 
                     // init by_extruder item only if we actually use the extruder
                     int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1);
@@ -1342,28 +1342,27 @@ void GCode::process_layer(
             gcode += "; INFILL WIPING STARTS\n";
             if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange
                 for (const auto& layer_to_print : layers) {     // iterate through all objects
-                gcode+="objekt\n";
                     if (layer_to_print.object_layer == nullptr)
                         continue;
-                    std::vector<ObjectByExtruder::Island::Region> overridden;
-                    for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
-                        gcode+="region\n";
-                        ObjectByExtruder::Island::Region new_region;
-                        overridden.push_back(new_region);
-                        for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
-                            gcode+="entity\n";
-                            auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                            if (fill->get_extruder_override() == extruder_id) {
-                                gcode+="*\n";
-                                overridden.back().infills.append(*fill);
-                                fill->set_extruder_override(-1);
-                            }
-                       }
-                    }
+
                     m_config.apply((layer_to_print.object_layer)->object()->config, true);
-                    Point copy = (layer_to_print.object_layer)->object()->_shifted_copies.front();
-                    this->set_origin(unscale(copy.x), unscale(copy.y));
-                    gcode += this->extrude_infill(print, overridden);
+
+                    for (unsigned copy_id = 0; copy_id < layer_to_print.object()->copies().size(); ++copy_id) {
+                        std::vector<ObjectByExtruder::Island::Region> overridden;
+                        for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
+                            ObjectByExtruder::Island::Region new_region;
+                            overridden.push_back(new_region);
+                            for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
+                                auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                                if (fill->get_extruder_override(copy_id) == extruder_id)
+                                        overridden.back().infills.append(*fill);
+                           }
+                        }
+
+                        Point copy = (layer_to_print.object_layer)->object()->_shifted_copies[copy_id];
+                        this->set_origin(unscale(copy.x), unscale(copy.y));
+                        gcode += this->extrude_infill(print, overridden);
+                    }
                 }
             }
             gcode += "; WIPING FINISHED\n";
@@ -1393,6 +1392,7 @@ void GCode::process_layer(
             // Sort the copies by the closest point starting with the current print position.
             
 
+            unsigned int copy_id = 0;
             for (const Point &copy : copies) {
                 // When starting a new object, use the external motion planner for the first travel move.
                 std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
@@ -1409,13 +1409,14 @@ void GCode::process_layer(
                 }
                 for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
                     if (print.config.infill_first) {
-                        gcode += this->extrude_infill(print, island.by_region);
+                        gcode += this->extrude_infill(print, island.by_region_special(copy_id));
                         gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
                     } else {
                         gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
-                        gcode += this->extrude_infill(print, island.by_region);
+                        gcode += this->extrude_infill(print, island.by_region_special(copy_id));
                     }
                 }
+                ++copy_id;
             }
         }
     }
@@ -2042,7 +2043,6 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
 }
 
 // Chain the paths hierarchically by a greedy algorithm to minimize a travel distance.
-// if extruder_id is set, only entities marked with given extruder_id are extruded
 std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region)
 {
     std::string gcode;
@@ -2477,4 +2477,19 @@ Point GCode::gcode_to_point(const Pointf &point) const
         scale_(point.y - m_origin.y + extruder_offset.y));
 }
 
+
+std::vector<GCode::ObjectByExtruder::Island::Region> GCode::ObjectByExtruder::Island::by_region_special(unsigned int copy) const
+{
+    std::vector<ObjectByExtruder::Island::Region> out;
+    for (const auto& reg : by_region) {
+        out.push_back(ObjectByExtruder::Island::Region());
+        out.back().perimeters.append(reg.perimeters);
+
+        for (const auto& ee : reg.infills.entities)
+            if (ee->get_extruder_override(copy) == -1)
+                out.back().infills.append(*ee);
+    }
+    return out;
 }
+
+}   // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index d028e90aa..7716de8b8 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -217,9 +217,12 @@ protected:
                 ExtrusionEntityCollection infills;
             };
             std::vector<Region> by_region;
+            std::vector<Region> by_region_special(unsigned int copy) const;
         };
         std::vector<Island>         islands;
     };
+
+
     std::string     extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
     std::string     extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
     std::string     extrude_support(const ExtrusionEntityCollection &support_fills);
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 45d28e839..4da100768 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -21,7 +21,6 @@ TODO LIST
 #include <iostream>
 #include <vector>
 #include <numeric>
-#include <algorithm>
 
 #include "Analyzer.hpp"
 
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 54cb51658..a821b2024 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -5,6 +5,7 @@
 #include <string>
 #include <sstream>
 #include <utility>
+#include <algorithm>
 
 #include "WipeTower.hpp"
 
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 7e5ac0812..6a079b7d9 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1183,34 +1183,40 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
     if (!config.filament_soluble.get_at(new_extruder)) {                            // Soluble filament cannot be wiped in a random infill
         for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
             Layer* this_layer = nullptr;
-            for (unsigned int a = 0; a < objects[i]->layers.size(); this_layer = objects[i]->layers[++a]) // Finds this layer
-                if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON)
+            for (unsigned int a = 0; a < objects[i]->layers.size(); ++a) // Finds this layer
+                if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON) {
+                    this_layer = objects[i]->layers[a];
                     break;
-
-            for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
-                unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
-                if (config.filament_soluble.get_at(region_extruder)) // if this infill is meant to be soluble, keep it that way
-                    continue;
-
-                if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded)
-                    bool unused_yet = false;
-                    for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
-                        if (layer_tools.extruders[i] == new_extruder)
-                            unused_yet = true;
-                        if (layer_tools.extruders[i] == region_extruder)
-                            break;
-                    }
-                    if (unused_yet)
-                        continue;
                 }
+            if (this_layer == nullptr)
+                continue;
 
-                ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
-                for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
-                    auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                    if (fill->role() == erTopSolidInfill) continue;         // color of TopSolidInfill cannot be changed - it is visible
-                    if (volume_to_wipe > 0.f && !fill->is_extruder_overridden() && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
-                        fill->set_extruder_override(new_extruder);
-                        volume_to_wipe -= fill->total_volume();
+            for (unsigned int copy = 0; copy < objects[i]->copies().size(); ++copy) {    // iterate through copies first, so that we mark neighbouring infills
+                for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
+
+                    unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
+                    if (config.filament_soluble.get_at(region_extruder)) // if this infill is meant to be soluble, keep it that way
+                        continue;
+
+                    if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded)
+                        bool unused_yet = false;
+                        for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
+                            if (layer_tools.extruders[i] == new_extruder)
+                                unused_yet = true;
+                            if (layer_tools.extruders[i] == region_extruder)
+                                break;
+                        }
+                        if (unused_yet)
+                            continue;
+                    }
+                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
+                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
+                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue;         // these cannot be changed - it is / may be visible
+                            if (volume_to_wipe > 0.f && !fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                                fill->set_extruder_override(copy, new_extruder);
+                                volume_to_wipe -= fill->total_volume();
+                            }
                     }
                 }
             }

From a6c3acdf0209ed8e0c877d4b8e267bcafd72d6d4 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 1 Jun 2018 15:38:49 +0200
Subject: [PATCH 14/36] Wiping into infill - no infills are now inadvertedly
 printed twice (hopefully)

---
 xs/src/libslic3r/GCode.cpp | 30 ++++++++++++++++++++++--------
 xs/src/libslic3r/GCode.hpp |  3 ++-
 xs/src/libslic3r/Print.cpp |  1 +
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index bbca523e3..572f55adc 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1281,6 +1281,17 @@ void GCode::process_layer(
                             if (islands[i].by_region.empty())
                                 islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
                             islands[i].by_region[region_id].infills.append(fill->entities);
+
+                            // We just added fill->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
+                            // we will note their indices (for each copy separately):
+                            unsigned int last_added_entity_index = islands[i].by_region[region_id].infills.entities.size()-1;
+                            for (unsigned copy_id = 0; copy_id < layer_to_print.object()->copies().size(); ++copy_id) {
+                                if (islands[i].by_region[region_id].infills_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
+                                    islands[i].by_region[region_id].infills_per_copy_ids.push_back(std::vector<unsigned int>());
+                                if (!fill->is_extruder_overridden(copy_id))
+                                    for (int j=0; j<fill->entities.size(); ++j)
+                                        islands[i].by_region[region_id].infills_per_copy_ids.back().push_back(last_added_entity_index - j);
+                            }
                             break;
                         }
                 }
@@ -1409,11 +1420,11 @@ void GCode::process_layer(
                 }
                 for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
                     if (print.config.infill_first) {
-                        gcode += this->extrude_infill(print, island.by_region_special(copy_id));
+                        gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
                         gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
                     } else {
                         gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
-                        gcode += this->extrude_infill(print, island.by_region_special(copy_id));
+                        gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
                     }
                 }
                 ++copy_id;
@@ -2478,16 +2489,19 @@ Point GCode::gcode_to_point(const Pointf &point) const
 }
 
 
-std::vector<GCode::ObjectByExtruder::Island::Region> GCode::ObjectByExtruder::Island::by_region_special(unsigned int copy) const
+// Goes through by_region std::vector and returns only a subvector of entities to be printed in usual time
+// i.e. not when it's going to be done during infill wiping
+std::vector<GCode::ObjectByExtruder::Island::Region> GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy) const
 {
     std::vector<ObjectByExtruder::Island::Region> out;
-    for (const auto& reg : by_region) {
+    for (auto& reg : by_region) {
         out.push_back(ObjectByExtruder::Island::Region());
-        out.back().perimeters.append(reg.perimeters);
+        out.back().perimeters.append(reg.perimeters);       // we will print all perimeters there are
 
-        for (const auto& ee : reg.infills.entities)
-            if (ee->get_extruder_override(copy) == -1)
-                out.back().infills.append(*ee);
+        if (!reg.infills_per_copy_ids.empty()) {
+            for (unsigned int i=0; i<reg.infills_per_copy_ids[copy].size(); ++i)
+                out.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
+        }
     }
     return out;
 }
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 7716de8b8..190f785fb 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -215,9 +215,10 @@ protected:
             struct Region {
                 ExtrusionEntityCollection perimeters;
                 ExtrusionEntityCollection infills;
+                std::vector<std::vector<unsigned int>> infills_per_copy_ids; // each member of the struct denotes first and one-past-last element to actually print
             };
             std::vector<Region> by_region;
-            std::vector<Region> by_region_special(unsigned int copy) const;
+            std::vector<Region> by_region_per_copy(unsigned int copy)  const; // returns only extrusions that are NOT printed during wiping into infill for this copy
         };
         std::vector<Island>         islands;
     };
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 6a079b7d9..cdb51999b 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1222,6 +1222,7 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
             }
         }
     }
+
     return std::max(0.f, volume_to_wipe);
 }
 

From bdaa1cbdfd1c92742b351bc772d63a740deae387 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 1 Jun 2018 15:38:49 +0200
Subject: [PATCH 15/36] Wiping into infill - no infills are now inadvertedly
 printed twice (hopefully)

---
 xs/src/libslic3r/GCode.cpp | 30 ++++++++++++++++++++++--------
 xs/src/libslic3r/GCode.hpp |  3 ++-
 xs/src/libslic3r/Print.cpp |  1 +
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index bbca523e3..572f55adc 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1281,6 +1281,17 @@ void GCode::process_layer(
                             if (islands[i].by_region.empty())
                                 islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
                             islands[i].by_region[region_id].infills.append(fill->entities);
+
+                            // We just added fill->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
+                            // we will note their indices (for each copy separately):
+                            unsigned int last_added_entity_index = islands[i].by_region[region_id].infills.entities.size()-1;
+                            for (unsigned copy_id = 0; copy_id < layer_to_print.object()->copies().size(); ++copy_id) {
+                                if (islands[i].by_region[region_id].infills_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
+                                    islands[i].by_region[region_id].infills_per_copy_ids.push_back(std::vector<unsigned int>());
+                                if (!fill->is_extruder_overridden(copy_id))
+                                    for (int j=0; j<fill->entities.size(); ++j)
+                                        islands[i].by_region[region_id].infills_per_copy_ids.back().push_back(last_added_entity_index - j);
+                            }
                             break;
                         }
                 }
@@ -1409,11 +1420,11 @@ void GCode::process_layer(
                 }
                 for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
                     if (print.config.infill_first) {
-                        gcode += this->extrude_infill(print, island.by_region_special(copy_id));
+                        gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
                         gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
                     } else {
                         gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
-                        gcode += this->extrude_infill(print, island.by_region_special(copy_id));
+                        gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
                     }
                 }
                 ++copy_id;
@@ -2478,16 +2489,19 @@ Point GCode::gcode_to_point(const Pointf &point) const
 }
 
 
-std::vector<GCode::ObjectByExtruder::Island::Region> GCode::ObjectByExtruder::Island::by_region_special(unsigned int copy) const
+// Goes through by_region std::vector and returns only a subvector of entities to be printed in usual time
+// i.e. not when it's going to be done during infill wiping
+std::vector<GCode::ObjectByExtruder::Island::Region> GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy) const
 {
     std::vector<ObjectByExtruder::Island::Region> out;
-    for (const auto& reg : by_region) {
+    for (auto& reg : by_region) {
         out.push_back(ObjectByExtruder::Island::Region());
-        out.back().perimeters.append(reg.perimeters);
+        out.back().perimeters.append(reg.perimeters);       // we will print all perimeters there are
 
-        for (const auto& ee : reg.infills.entities)
-            if (ee->get_extruder_override(copy) == -1)
-                out.back().infills.append(*ee);
+        if (!reg.infills_per_copy_ids.empty()) {
+            for (unsigned int i=0; i<reg.infills_per_copy_ids[copy].size(); ++i)
+                out.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
+        }
     }
     return out;
 }
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 7716de8b8..ec5d8f101 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -215,9 +215,10 @@ protected:
             struct Region {
                 ExtrusionEntityCollection perimeters;
                 ExtrusionEntityCollection infills;
+                std::vector<std::vector<unsigned int>> infills_per_copy_ids; // indices of infill.entities that are not part of infill wiping (an element for each object copy)
             };
             std::vector<Region> by_region;
-            std::vector<Region> by_region_special(unsigned int copy) const;
+            std::vector<Region> by_region_per_copy(unsigned int copy)  const; // returns only extrusions that are NOT printed during wiping into infill for this copy
         };
         std::vector<Island>         islands;
     };
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 6a079b7d9..cdb51999b 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1222,6 +1222,7 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
             }
         }
     }
+
     return std::max(0.f, volume_to_wipe);
 }
 

From 7c9d594ff60090337b8661bcbb6337b94841ec99 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 4 Jun 2018 12:15:59 +0200
Subject: [PATCH 16/36] Fixed behaviour of infill wiping for multiple copies of
 an object

---
 xs/src/libslic3r/GCode.cpp | 8 ++------
 xs/src/libslic3r/Print.cpp | 2 +-
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 572f55adc..7d4ecf0b4 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1262,9 +1262,6 @@ void GCode::process_layer(
                         // This shouldn't happen but first_point() would fail.
                         continue;
 
-                    /*if (fill->is_extruder_overridden())
-                        continue;*/
-
                     // init by_extruder item only if we actually use the extruder
                     int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1);
                     // Init by_extruder item only if we actually use the extruder.
@@ -1285,12 +1282,12 @@ void GCode::process_layer(
                             // We just added fill->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
                             // we will note their indices (for each copy separately):
                             unsigned int last_added_entity_index = islands[i].by_region[region_id].infills.entities.size()-1;
-                            for (unsigned copy_id = 0; copy_id < layer_to_print.object()->copies().size(); ++copy_id) {
+                            for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) {
                                 if (islands[i].by_region[region_id].infills_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
                                     islands[i].by_region[region_id].infills_per_copy_ids.push_back(std::vector<unsigned int>());
                                 if (!fill->is_extruder_overridden(copy_id))
                                     for (int j=0; j<fill->entities.size(); ++j)
-                                        islands[i].by_region[region_id].infills_per_copy_ids.back().push_back(last_added_entity_index - j);
+                                        islands[i].by_region[region_id].infills_per_copy_ids[copy_id].push_back(last_added_entity_index - j);
                             }
                             break;
                         }
@@ -1401,7 +1398,6 @@ void GCode::process_layer(
             else
                 copies.push_back(print_object->_shifted_copies[single_object_idx]);
             // Sort the copies by the closest point starting with the current print position.
-            
 
             unsigned int copy_id = 0;
             for (const Point &copy : copies) {
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index cdb51999b..e5a4f5dc7 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1191,7 +1191,7 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
             if (this_layer == nullptr)
                 continue;
 
-            for (unsigned int copy = 0; copy < objects[i]->copies().size(); ++copy) {    // iterate through copies first, so that we mark neighbouring infills
+            for (unsigned int copy = 0; copy < objects[i]->_shifted_copies.size(); ++copy) {    // iterate through copies first, so that we mark neighbouring infills
                 for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
 
                     unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based

From 4830593cacbe5d81ee499eda9581d616df5f0898 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 5 Jun 2018 12:50:34 +0200
Subject: [PATCH 17/36] Started to work on the 'wipe into dedicated object
 feature'

---
 xs/src/libslic3r/GCode.cpp | 40 ++++++++++++++++++++++++++++++++------
 xs/src/libslic3r/GCode.hpp |  3 ++-
 xs/src/libslic3r/Print.cpp | 20 ++++++++++++++++++-
 3 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 7d4ecf0b4..28a8d2e52 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1224,7 +1224,7 @@ void GCode::process_layer(
                 if (layerm == nullptr)
                     continue;
                 const PrintRegion &region = *print.regions[region_id];
-                
+
                 // process perimeters
                 for (const ExtrusionEntity *ee : layerm->perimeters.entities) {
                     // perimeter_coll represents perimeter extrusions of a single island.
@@ -1246,6 +1246,17 @@ void GCode::process_layer(
                             if (islands[i].by_region.empty())
                                 islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
                             islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities);
+
+                            // We just added perimeter_coll->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
+                            // we will note their indices (for each copy separately):
+                            unsigned int last_added_entity_index = islands[i].by_region[region_id].perimeters.entities.size()-1;
+                            for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) {
+                                if (islands[i].by_region[region_id].perimeters_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
+                                    islands[i].by_region[region_id].perimeters_per_copy_ids.push_back(std::vector<unsigned int>());
+                                if (!perimeter_coll->is_extruder_overridden(copy_id))
+                                    for (int j=0; j<perimeter_coll->entities.size(); ++j)
+                                        islands[i].by_region[region_id].perimeters_per_copy_ids[copy_id].push_back(last_added_entity_index - j);
+                            }
                             break;
                         }
                 }
@@ -1362,14 +1373,29 @@ void GCode::process_layer(
                             overridden.push_back(new_region);
                             for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
                                 auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                if (fill->get_extruder_override(copy_id) == extruder_id)
+                                if (fill->get_extruder_override(copy_id) == (unsigned int)extruder_id)
                                         overridden.back().infills.append(*fill);
                            }
+                           for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->perimeters.entities) {
+                                auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                                if (fill->get_extruder_override(copy_id) == (unsigned int)extruder_id)
+                                        overridden.back().perimeters.append((*fill).entities);
+                           }
                         }
 
                         Point copy = (layer_to_print.object_layer)->object()->_shifted_copies[copy_id];
                         this->set_origin(unscale(copy.x), unscale(copy.y));
-                        gcode += this->extrude_infill(print, overridden);
+
+
+                        std::unique_ptr<EdgeGrid::Grid> u;
+                        if (print.config.infill_first) {
+                            gcode += this->extrude_infill(print, overridden);
+                            gcode += this->extrude_perimeters(print, overridden, u);
+                        }
+                        else {
+                            gcode += this->extrude_perimeters(print, overridden, u);
+                            gcode += this->extrude_infill(print, overridden);
+                        }
                     }
                 }
             }
@@ -1417,9 +1443,9 @@ void GCode::process_layer(
                 for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
                     if (print.config.infill_first) {
                         gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
-                        gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
+                        gcode += this->extrude_perimeters(print, island.by_region_per_copy(copy_id), lower_layer_edge_grids[layer_id]);
                     } else {
-                        gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
+                        gcode += this->extrude_perimeters(print, island.by_region_per_copy(copy_id), lower_layer_edge_grids[layer_id]);
                         gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
                     }
                 }
@@ -2492,11 +2518,13 @@ std::vector<GCode::ObjectByExtruder::Island::Region> GCode::ObjectByExtruder::Is
     std::vector<ObjectByExtruder::Island::Region> out;
     for (auto& reg : by_region) {
         out.push_back(ObjectByExtruder::Island::Region());
-        out.back().perimeters.append(reg.perimeters);       // we will print all perimeters there are
+        //out.back().perimeters.append(reg.perimeters);       // we will print all perimeters there are
 
         if (!reg.infills_per_copy_ids.empty()) {
             for (unsigned int i=0; i<reg.infills_per_copy_ids[copy].size(); ++i)
                 out.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
+            for (unsigned int i=0; i<reg.perimeters_per_copy_ids[copy].size(); ++i)
+                out.back().perimeters.append(*(reg.perimeters.entities[reg.perimeters_per_copy_ids[copy][i]]));
         }
     }
     return out;
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index ec5d8f101..1f5e800ea 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -215,7 +215,8 @@ protected:
             struct Region {
                 ExtrusionEntityCollection perimeters;
                 ExtrusionEntityCollection infills;
-                std::vector<std::vector<unsigned int>> infills_per_copy_ids; // indices of infill.entities that are not part of infill wiping (an element for each object copy)
+                std::vector<std::vector<unsigned int>> infills_per_copy_ids;    // indices of infill.entities that are not part of infill wiping (an element for each object copy)
+                std::vector<std::vector<unsigned int>> perimeters_per_copy_ids; // indices of infill.entities that are not part of infill wiping (an element for each object copy)
             };
             std::vector<Region> by_region;
             std::vector<Region> by_region_per_copy(unsigned int copy)  const; // returns only extrusions that are NOT printed during wiping into infill for this copy
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index e5a4f5dc7..4b52e2507 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1209,11 +1209,29 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
                         if (unused_yet)
                             continue;
                     }
+
+                    //if (object.wipe_into_perimeters)
+                    {
+                        ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
+                        for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
+                            auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                                if (volume_to_wipe <= 0.f)
+                                    break;
+                                if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {
+                                    fill->set_extruder_override(copy, new_extruder);
+                                    volume_to_wipe -= fill->total_volume();
+                                }
+                        }
+                    }
+
+
                     ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
                     for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
                         auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
                         if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue;         // these cannot be changed - it is / may be visible
-                            if (volume_to_wipe > 0.f && !fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                            if (volume_to_wipe <= 0.f)
+                                break;
+                            if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
                                 fill->set_extruder_override(copy, new_extruder);
                                 volume_to_wipe -= fill->total_volume();
                             }

From 73452fd79db41286e6c04658edf6b0e15ce8f008 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 6 Jun 2018 18:24:42 +0200
Subject: [PATCH 18/36] More progress on 'wipe into dedicated object' feature
 (e.g. new value in object settings)

---
 xs/src/libslic3r/GCode.cpp       | 27 +++++++++++++++++----------
 xs/src/libslic3r/GCode.hpp       |  8 ++++++--
 xs/src/libslic3r/Print.cpp       | 27 +++++++++++++--------------
 xs/src/libslic3r/PrintConfig.cpp | 10 ++++++++++
 xs/src/libslic3r/PrintConfig.hpp |  6 ++++--
 xs/src/slic3r/GUI/Preset.cpp     |  2 +-
 xs/src/slic3r/GUI/Tab.cpp        |  1 +
 7 files changed, 52 insertions(+), 29 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 28a8d2e52..92898c820 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1407,7 +1407,7 @@ void GCode::process_layer(
         auto objects_by_extruder_it = by_extruder.find(extruder_id);
         if (objects_by_extruder_it == by_extruder.end())
             continue;
-        for (const ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
+        for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
             const size_t       layer_id     = &object_by_extruder - objects_by_extruder_it->second.data();
             const PrintObject *print_object = layers[layer_id].object();
             if (print_object == nullptr)
@@ -1440,7 +1440,7 @@ void GCode::process_layer(
                         object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
                     m_layer = layers[layer_id].layer();
                 }
-                for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
+                for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
                     if (print.config.infill_first) {
                         gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
                         gcode += this->extrude_perimeters(print, island.by_region_per_copy(copy_id), lower_layer_edge_grids[layer_id]);
@@ -2511,23 +2511,30 @@ Point GCode::gcode_to_point(const Pointf &point) const
 }
 
 
-// Goes through by_region std::vector and returns only a subvector of entities to be printed in usual time
+// Goes through by_region std::vector and returns ref a subvector of entities to be printed in usual time
 // i.e. not when it's going to be done during infill wiping
-std::vector<GCode::ObjectByExtruder::Island::Region> GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy) const
+const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy)
 {
-    std::vector<ObjectByExtruder::Island::Region> out;
-    for (auto& reg : by_region) {
-        out.push_back(ObjectByExtruder::Island::Region());
+    if (copy == last_copy)
+        return by_region_per_copy_cache;
+    else {
+        by_region_per_copy_cache.clear();
+        last_copy = copy;
+    }
+
+    //std::vector<ObjectByExtruder::Island::Region> out;
+    for (const auto& reg : by_region) {
+        by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region());
         //out.back().perimeters.append(reg.perimeters);       // we will print all perimeters there are
 
         if (!reg.infills_per_copy_ids.empty()) {
             for (unsigned int i=0; i<reg.infills_per_copy_ids[copy].size(); ++i)
-                out.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
+                by_region_per_copy_cache.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
             for (unsigned int i=0; i<reg.perimeters_per_copy_ids[copy].size(); ++i)
-                out.back().perimeters.append(*(reg.perimeters.entities[reg.perimeters_per_copy_ids[copy][i]]));
+                by_region_per_copy_cache.back().perimeters.append(*(reg.perimeters.entities[reg.perimeters_per_copy_ids[copy][i]]));
         }
     }
-    return out;
+    return by_region_per_copy_cache;
 }
 
 }   // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 1f5e800ea..35f80b578 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -218,8 +218,12 @@ protected:
                 std::vector<std::vector<unsigned int>> infills_per_copy_ids;    // indices of infill.entities that are not part of infill wiping (an element for each object copy)
                 std::vector<std::vector<unsigned int>> perimeters_per_copy_ids; // indices of infill.entities that are not part of infill wiping (an element for each object copy)
             };
-            std::vector<Region> by_region;
-            std::vector<Region> by_region_per_copy(unsigned int copy)  const; // returns only extrusions that are NOT printed during wiping into infill for this copy
+            std::vector<Region> by_region;                                    // all extrusions for this island, grouped by regions
+            const std::vector<Region>& by_region_per_copy(unsigned int copy); // returns reference to subvector of by_region (only extrusions that are NOT printed during wiping into infill for this copy)
+
+        private:
+            std::vector<Region> by_region_per_copy_cache;   // caches vector generated by function above to avoid copying and recalculating
+            unsigned int last_copy = (unsigned int)(-1);    // index of last copy that by_region_per_copy was called for
         };
         std::vector<Island>         islands;
     };
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 4b52e2507..940bdc2a2 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1210,7 +1210,19 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
                             continue;
                     }
 
-                    //if (object.wipe_into_perimeters)
+                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
+                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
+                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue;         // these cannot be changed - it is / may be visible
+                            if (volume_to_wipe <= 0.f)
+                                break;
+                            if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                                fill->set_extruder_override(copy, new_extruder);
+                                volume_to_wipe -= fill->total_volume();
+                            }
+                    }
+
+                    if (objects[i]->config.wipe_into_objects)
                     {
                         ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
                         for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
@@ -1223,19 +1235,6 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
                                 }
                         }
                     }
-
-
-                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
-                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
-                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue;         // these cannot be changed - it is / may be visible
-                            if (volume_to_wipe <= 0.f)
-                                break;
-                            if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
-                                fill->set_extruder_override(copy, new_extruder);
-                                volume_to_wipe -= fill->total_volume();
-                            }
-                    }
                 }
             }
         }
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index bf9421f9d..98b111a4d 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1893,6 +1893,16 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "wipe-into-infill!";
     def->default_value = new ConfigOptionBool(true);
 
+    def = this->add("wipe_into_objects", coBool);
+    def->category = L("Extruders");
+    def->label = L("Wiping into objects");
+    def->tooltip = L("Objects will be used to wipe the nozzle after a toolchange to save material "
+                     "that would otherwise end up in the wipe tower and decrease print time. "
+                     "Colours of the objects will be mixed as a result. (This setting is usually "
+                     "used on per-object basis.)");
+    def->cli = "wipe-into-objects!";
+    def->default_value = new ConfigOptionBool(false);
+
     def = this->add("wipe_tower_bridging", coFloat);
     def->label = L("Maximal bridging distance");
     def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 1b73c31b3..f638a7674 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -336,7 +336,8 @@ public:
     ConfigOptionBool                support_material_with_sheath;
     ConfigOptionFloatOrPercent      support_material_xy_spacing;
     ConfigOptionFloat               xy_size_compensation;
-    
+    ConfigOptionBool                wipe_into_objects;
+
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
     {
@@ -372,6 +373,7 @@ protected:
         OPT_PTR(support_material_threshold);
         OPT_PTR(support_material_with_sheath);
         OPT_PTR(xy_size_compensation);
+        OPT_PTR(wipe_into_objects);
     }
 };
 
@@ -414,7 +416,7 @@ public:
     ConfigOptionFloatOrPercent      top_infill_extrusion_width;
     ConfigOptionInt                 top_solid_layers;
     ConfigOptionFloatOrPercent      top_solid_infill_speed;
-
+    
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
     {
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index e483381ac..84f685533 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -298,7 +298,7 @@ const std::vector<std::string>& Preset::print_options()
         "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", 
         "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", 
         "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
-        "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "wipe_into_infill", "compatible_printers",
+        "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "wipe_into_infill", "wipe_into_objects", "compatible_printers",
         "compatible_printers_condition","inherits"
     };
     return s_opts;
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index c94307aa4..4974e9377 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -946,6 +946,7 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
         optgroup->append_single_option_line("wipe_tower_bridging");
         optgroup->append_single_option_line("wipe_into_infill");
+        optgroup->append_single_option_line("wipe_into_objects");
 
 		optgroup = page->new_optgroup(_(L("Advanced")));
 		optgroup->append_single_option_line("interface_shells");

From b6455b66bd7894b8d575ba91524aa93dc306888d Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 7 Jun 2018 16:19:57 +0200
Subject: [PATCH 19/36] Wiping into infill/objects - invalidation of the wipe
 tower, bugfixes

---
 xs/src/libslic3r/GCode.cpp       | 11 ++++---
 xs/src/libslic3r/Print.cpp       | 56 +++++++++++++++++++++++---------
 xs/src/libslic3r/Print.hpp       |  5 ++-
 xs/src/libslic3r/PrintConfig.cpp |  1 +
 xs/src/libslic3r/PrintConfig.hpp |  4 +--
 xs/src/libslic3r/PrintObject.cpp |  7 +++-
 6 files changed, 60 insertions(+), 24 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 92898c820..ce63a374c 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1357,7 +1357,7 @@ void GCode::process_layer(
             m_avoid_crossing_perimeters.disable_once = true;
         }
 
-        if (print.config.wipe_into_infill.value) {
+        {
             gcode += "; INFILL WIPING STARTS\n";
             if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange
                 for (const auto& layer_to_print : layers) {     // iterate through all objects
@@ -1373,12 +1373,12 @@ void GCode::process_layer(
                             overridden.push_back(new_region);
                             for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
                                 auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                if (fill->get_extruder_override(copy_id) == (unsigned int)extruder_id)
+                                if (fill->get_extruder_override(copy_id) == (int)extruder_id)
                                         overridden.back().infills.append(*fill);
                            }
                            for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->perimeters.entities) {
                                 auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                if (fill->get_extruder_override(copy_id) == (unsigned int)extruder_id)
+                                if (fill->get_extruder_override(copy_id) == (int)extruder_id)
                                         overridden.back().perimeters.append((*fill).entities);
                            }
                         }
@@ -2527,12 +2527,13 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru
         by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region());
         //out.back().perimeters.append(reg.perimeters);       // we will print all perimeters there are
 
-        if (!reg.infills_per_copy_ids.empty()) {
+        if (!reg.infills_per_copy_ids.empty())
             for (unsigned int i=0; i<reg.infills_per_copy_ids[copy].size(); ++i)
                 by_region_per_copy_cache.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
+
+        if (!reg.perimeters_per_copy_ids.empty())
             for (unsigned int i=0; i<reg.perimeters_per_copy_ids[copy].size(); ++i)
                 by_region_per_copy_cache.back().perimeters.append(*(reg.perimeters.entities[reg.perimeters_per_copy_ids[copy][i]]));
-        }
     }
     return by_region_per_copy_cache;
 }
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 940bdc2a2..5664bd5b1 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -160,6 +160,11 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
     std::vector<PrintStep> steps;
     std::vector<PrintObjectStep> osteps;
     bool invalidated = false;
+
+    // Always invalidate the wipe tower. This is probably necessary because of the wipe_into_infill / wipe_into_objects
+    // features - nearly anything can influence what should (and could) be wiped into.
+    steps.emplace_back(psWipeTower);
+
     for (const t_config_option_key &opt_key : opt_keys) {
         if (steps_ignore.find(opt_key) != steps_ignore.end()) {
             // These options only affect G-code export or they are just notes without influence on the generated G-code,
@@ -201,7 +206,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "wipe_tower_rotation_angle"
             || opt_key == "wipe_tower_bridging"
             || opt_key == "wiping_volumes_matrix"
-            || opt_key == "parking_pos_retraction"
+            || opt_key == u8"parking_pos_retraction"
             || opt_key == "cooling_tube_retraction"
             || opt_key == "cooling_tube_length"
             || opt_key == "extra_loading_move"
@@ -216,7 +221,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             osteps.emplace_back(posSupportMaterial);
             steps.emplace_back(psSkirt);
             steps.emplace_back(psBrim);
-            steps.emplace_back(psWipeTower);
         } else {
             // for legacy, if we can't handle this option let's invalidate all steps
             //FIXME invalidate all steps of all objects as well?
@@ -1125,6 +1129,7 @@ void Print::_make_wipe_tower()
     m_wipe_tower_priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
         wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full));
 
+    reset_wiping_extrusions(); // if this is not the first time the wipe tower is generated, some extrusions might remember their last wiping status
 
     // Lets go through the wipe tower layers and determine pairs of extruder changes for each
     // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
@@ -1138,8 +1143,8 @@ void Print::_make_wipe_tower()
                 if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];    // total volume to wipe after this toolchange
 
-                    if (config.wipe_into_infill && !first_layer)
-                        volume_to_wipe = mark_wiping_infill(layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
+                    if (!first_layer)   // unless we're on the first layer, try to assign some infills/objects for the wiping:
+                        volume_to_wipe = mark_wiping_extrusions(layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
 
                     wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
                     current_extruder_id = extruder_id;
@@ -1176,12 +1181,31 @@ void Print::_make_wipe_tower()
 
 
 
-float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
+void Print::reset_wiping_extrusions() {
+     for (size_t i = 0; i < objects.size(); ++ i) {
+        for (auto& this_layer : objects[i]->layers) {
+            for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
+                for (unsigned int copy = 0; copy < objects[i]->_shifted_copies.size(); ++copy) {
+                    this_layer->regions[region_id]->fills.set_extruder_override(copy, -1);
+                    this_layer->regions[region_id]->perimeters.set_extruder_override(copy, -1);
+                }
+            }
+        }
+     }
+}
+
+
+
+float Print::mark_wiping_extrusions(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
 {
     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 
     if (!config.filament_soluble.get_at(new_extruder)) {                            // Soluble filament cannot be wiped in a random infill
         for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
+
+            if (!objects[i]->config.wipe_into_infill && !objects[i]->config.wipe_into_objects)
+                continue;
+
             Layer* this_layer = nullptr;
             for (unsigned int a = 0; a < objects[i]->layers.size(); ++a) // Finds this layer
                 if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON) {
@@ -1210,16 +1234,18 @@ float Print::mark_wiping_infill(const ToolOrdering::LayerTools& layer_tools, uns
                             continue;
                     }
 
-                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
-                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
-                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue;         // these cannot be changed - it is / may be visible
-                            if (volume_to_wipe <= 0.f)
-                                break;
-                            if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
-                                fill->set_extruder_override(copy, new_extruder);
-                                volume_to_wipe -= fill->total_volume();
-                            }
+                    if (objects[i]->config.wipe_into_infill) {
+                        ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
+                        for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
+                            auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                            if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue;         // these cannot be changed - it is / may be visible
+                                if (volume_to_wipe <= 0.f)
+                                    break;
+                                if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                                    fill->set_extruder_override(copy, new_extruder);
+                                    volume_to_wipe -= fill->total_volume();
+                                }
+                        }
                     }
 
                     if (objects[i]->config.wipe_into_objects)
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 77b47fb83..77787063e 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -317,7 +317,10 @@ private:
 
     // 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_infill(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
+    float mark_wiping_extrusions(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
+
+    // A function to go through all entities and unsets their extruder_override flag
+    void reset_wiping_extrusions();
 
     // Has the calculation been canceled?
     tbb::atomic<bool>   m_canceled;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 98b111a4d..d00f7974e 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1886,6 +1886,7 @@ PrintConfigDef::PrintConfigDef()
     def->default_value = new ConfigOptionFloat(0.);
 
     def = this->add("wipe_into_infill", coBool);
+    def->category = L("Extruders");
     def->label = L("Wiping into infill");
     def->tooltip = L("Wiping after toolchange will be preferentially done inside infills. "
                      "This lowers the amount of waste but may result in longer print time "
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index f638a7674..92ead2927 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -337,6 +337,7 @@ public:
     ConfigOptionFloatOrPercent      support_material_xy_spacing;
     ConfigOptionFloat               xy_size_compensation;
     ConfigOptionBool                wipe_into_objects;
+    ConfigOptionBool                wipe_into_infill;
 
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
@@ -374,6 +375,7 @@ protected:
         OPT_PTR(support_material_with_sheath);
         OPT_PTR(xy_size_compensation);
         OPT_PTR(wipe_into_objects);
+        OPT_PTR(wipe_into_infill);
     }
 };
 
@@ -644,7 +646,6 @@ public:
     ConfigOptionFloat               wipe_tower_per_color_wipe;
     ConfigOptionFloat               wipe_tower_rotation_angle;
     ConfigOptionFloat               wipe_tower_bridging;
-    ConfigOptionBool                wipe_into_infill;
     ConfigOptionFloats              wiping_volumes_matrix;
     ConfigOptionFloats              wiping_volumes_extruders;
     ConfigOptionFloat               z_offset;
@@ -713,7 +714,6 @@ protected:
         OPT_PTR(wipe_tower_width);
         OPT_PTR(wipe_tower_per_color_wipe);
         OPT_PTR(wipe_tower_rotation_angle);
-        OPT_PTR(wipe_into_infill);
         OPT_PTR(wipe_tower_bridging);
         OPT_PTR(wiping_volumes_matrix);
         OPT_PTR(wiping_volumes_extruders);
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index b0341db16..1c403acdb 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -231,7 +231,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
             || opt_key == "perimeter_speed"
             || opt_key == "small_perimeter_speed"
             || opt_key == "solid_infill_speed"
-            || opt_key == "top_solid_infill_speed") {
+            || opt_key == "top_solid_infill_speed"
+            || opt_key == "wipe_into_infill"    // when these these two are changed, we only need to invalidate the wipe tower,
+            || opt_key == "wipe_into_objects"   // which we already did at the very beginning - nothing more to be done
+            ) {
             // these options only affect G-code export, so nothing to invalidate
         } else {
             // for legacy, if we can't handle this option let's invalidate all steps
@@ -271,6 +274,8 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
     }
 
     // Wipe tower depends on the ordering of extruders, which in turn depends on everything.
+    // It also decides about what the wipe_into_infill / wipe_into_object features will do,
+    // and that too depends on many of the settings.
     invalidated |= this->_print->invalidate_step(psWipeTower);
     return invalidated;
 }

From 29dd305aaa4b738eaef91bd2de82e667a17d89fa Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 13 Jun 2018 11:48:43 +0200
Subject: [PATCH 20/36] Wiping into perimeters - bugfix (wrong order of
 perimeters and infills)

---
 xs/src/libslic3r/GCode.cpp | 17 ++++++++---------
 xs/src/libslic3r/Print.cpp | 21 ++++++++++++++++++++-
 2 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index ce63a374c..809745da2 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1249,13 +1249,13 @@ void GCode::process_layer(
 
                             // We just added perimeter_coll->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
                             // we will note their indices (for each copy separately):
-                            unsigned int last_added_entity_index = islands[i].by_region[region_id].perimeters.entities.size()-1;
+                            unsigned int first_added_entity_index = islands[i].by_region[region_id].perimeters.entities.size() - perimeter_coll->entities.size();
                             for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) {
                                 if (islands[i].by_region[region_id].perimeters_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
                                     islands[i].by_region[region_id].perimeters_per_copy_ids.push_back(std::vector<unsigned int>());
                                 if (!perimeter_coll->is_extruder_overridden(copy_id))
-                                    for (int j=0; j<perimeter_coll->entities.size(); ++j)
-                                        islands[i].by_region[region_id].perimeters_per_copy_ids[copy_id].push_back(last_added_entity_index - j);
+                                    for (int j=first_added_entity_index; j<islands[i].by_region[region_id].perimeters.entities.size(); ++j)
+                                        islands[i].by_region[region_id].perimeters_per_copy_ids[copy_id].push_back(j);
                             }
                             break;
                         }
@@ -1292,13 +1292,13 @@ void GCode::process_layer(
 
                             // We just added fill->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
                             // we will note their indices (for each copy separately):
-                            unsigned int last_added_entity_index = islands[i].by_region[region_id].infills.entities.size()-1;
+                            unsigned int first_added_entity_index = islands[i].by_region[region_id].infills.entities.size() - fill->entities.size();
                             for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) {
                                 if (islands[i].by_region[region_id].infills_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
                                     islands[i].by_region[region_id].infills_per_copy_ids.push_back(std::vector<unsigned int>());
                                 if (!fill->is_extruder_overridden(copy_id))
-                                    for (int j=0; j<fill->entities.size(); ++j)
-                                        islands[i].by_region[region_id].infills_per_copy_ids[copy_id].push_back(last_added_entity_index - j);
+                                    for (int j=first_added_entity_index; j<islands[i].by_region[region_id].infills.entities.size(); ++j)
+                                        islands[i].by_region[region_id].infills_per_copy_ids[copy_id].push_back(j);
                             }
                             break;
                         }
@@ -1357,6 +1357,7 @@ void GCode::process_layer(
             m_avoid_crossing_perimeters.disable_once = true;
         }
 
+        if (layer_tools.has_wipe_tower)   // the infill/perimeter wiping to save the material on the wipe tower
         {
             gcode += "; INFILL WIPING STARTS\n";
             if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange
@@ -2511,7 +2512,7 @@ Point GCode::gcode_to_point(const Pointf &point) const
 }
 
 
-// Goes through by_region std::vector and returns ref a subvector of entities to be printed in usual time
+// Goes through by_region std::vector and returns reference to a subvector of entities to be printed in usual time
 // i.e. not when it's going to be done during infill wiping
 const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy)
 {
@@ -2522,10 +2523,8 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru
         last_copy = copy;
     }
 
-    //std::vector<ObjectByExtruder::Island::Region> out;
     for (const auto& reg : by_region) {
         by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region());
-        //out.back().perimeters.append(reg.perimeters);       // we will print all perimeters there are
 
         if (!reg.infills_per_copy_ids.empty())
             for (unsigned int i=0; i<reg.infills_per_copy_ids[copy].size(); ++i)
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 5664bd5b1..d448ab2f3 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -206,7 +206,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "wipe_tower_rotation_angle"
             || opt_key == "wipe_tower_bridging"
             || opt_key == "wiping_volumes_matrix"
-            || opt_key == u8"parking_pos_retraction"
+            || opt_key == "parking_pos_retraction"
             || opt_key == "cooling_tube_retraction"
             || opt_key == "cooling_tube_length"
             || opt_key == "extra_loading_move"
@@ -1196,6 +1196,25 @@ void Print::reset_wiping_extrusions() {
 
 
 
+// Strategy for  wiping (TODO):
+// if !infill_first
+//      start with dedicated objects
+//            print a perimeter and its corresponding infill immediately after
+//            repeat until there are no dedicated objects left
+//            if there are some left and this is the last toolchange on the layer, mark all remaining extrusions of the object (so we don't have to travel back to it later)
+//      move to normal objects
+//            start with one object and start assigning its infill, if their perimeters ARE ALREADY EXTRUDED
+//            never touch perimeters
+//
+// if infill first
+//        start with dedicated objects
+//            print an infill and its corresponding perimeter immediately after
+//            repeat until you run out of infills
+//        move to normal objects
+//            start assigning infills (one copy after another)
+//            repeat until you run out of infills, leave perimeters be
+
+
 float Print::mark_wiping_extrusions(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
 {
     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this

From 8a47852be22b3be0d02d36dd6a50d7674ed465fe Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 20 Jun 2018 12:52:00 +0200
Subject: [PATCH 21/36] Refactoring of perimeters/infills wiping
 (ToolOrdering::WipingExtrusions now takes care of the agenda)

Squashed commit of the following:

commit 931eb2684103e8571b4a2e9804765fef268361c3
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Wed Jun 20 12:50:27 2018 +0200

    ToolOrdering::WipingExtrusions now holds all information necessary for infill/perimeter wiping

commit cc8becfbdd771f7e279434c8bd6be147e4b321ee
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Tue Jun 19 10:52:03 2018 +0200

    Wiping is now done as normal print would be (less extra code in process_layer)

commit 1b120754b0691cce46ee5e10f3840480c559ac1f
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Fri Jun 15 15:55:15 2018 +0200

    Refactoring: ObjectByExtruder changed so that it is aware of the wiping extrusions

commit 1641e326bb5e0a0c69d6bfc6efa23153dc2e4543
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Thu Jun 14 12:22:18 2018 +0200

    Refactoring: new class WipingExtrusion in ToolOrdering.hpp
---
 xs/src/libslic3r/ExtrusionEntity.hpp          |  13 -
 .../libslic3r/ExtrusionEntityCollection.hpp   |   7 -
 xs/src/libslic3r/GCode.cpp                    | 321 ++++++++----------
 xs/src/libslic3r/GCode.hpp                    |  13 +-
 xs/src/libslic3r/GCode/ToolOrdering.cpp       |  38 +++
 xs/src/libslic3r/GCode/ToolOrdering.hpp       |  37 +-
 xs/src/libslic3r/Print.cpp                    | 170 +++++-----
 xs/src/libslic3r/Print.hpp                    |   5 +-
 8 files changed, 301 insertions(+), 303 deletions(-)

diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index c0f681de5..15363e8ed 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -93,19 +93,6 @@ public:
     virtual Polyline as_polyline() const = 0;
     virtual double length() const = 0;
     virtual double total_volume() const = 0;
-
-    void set_entity_extruder_override(unsigned int copy, int extruder) {
-        if (copy+1 > extruder_override.size())
-            extruder_override.resize(copy+1, -1); // copy is zero-based index
-        extruder_override[copy] = extruder;
-    }
-    virtual int get_extruder_override(unsigned int copy) const   { try { return extruder_override.at(copy);       } catch (...) { return -1;    } }
-    virtual bool is_extruder_overridden(unsigned int copy) const { try { return extruder_override.at(copy) != -1; } catch (...) { return false; } }
-
-private:
-    // Set this variable to explicitly state you want to use specific extruder for thie EE (used for MM infill wiping)
-    // Each member of the vector corresponds to the respective copy of the object
-    std::vector<int> extruder_override;
 };
 
 typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index ee4b75f38..382455fe3 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -90,13 +90,6 @@ public:
         CONFESS("Calling length() on a ExtrusionEntityCollection");
         return 0.;        
     }
-
-    void set_extruder_override(unsigned int copy, int extruder) {
-        for (ExtrusionEntity* member : entities)
-            member->set_entity_extruder_override(copy, extruder);
-    }
-    virtual int get_extruder_override(unsigned int copy) const   { return entities.front()->get_extruder_override(copy);  }
-    virtual bool is_extruder_overridden(unsigned int copy) const { return entities.front()->is_extruder_overridden(copy); }
 };
 
 }
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 809745da2..cd27e3edd 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1147,7 +1147,6 @@ void GCode::process_layer(
 
     // Group extrusions by an extruder, then by an object, an island and a region.
     std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
-    
     for (const LayerToPrint &layer_to_print : layers) {
         if (layer_to_print.support_layer != nullptr) {
             const SupportLayer &support_layer = *layer_to_print.support_layer;
@@ -1225,92 +1224,63 @@ void GCode::process_layer(
                     continue;
                 const PrintRegion &region = *print.regions[region_id];
 
-                // process perimeters
-                for (const ExtrusionEntity *ee : layerm->perimeters.entities) {
-                    // perimeter_coll represents perimeter extrusions of a single island.
-                    const auto *perimeter_coll = dynamic_cast<const ExtrusionEntityCollection*>(ee);
-                    if (perimeter_coll->entities.empty())
-                        // This shouldn't happen but first_point() would fail.
-                        continue;
-                    // Init by_extruder item only if we actually use the extruder.
-                    std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
-                        by_extruder,
-                        std::max<int>(region.config.perimeter_extruder.value - 1, 0),
-                        &layer_to_print - layers.data(),
-                        layers.size(), n_slices+1);
-                    for (size_t i = 0; i <= n_slices; ++ i)
-                        if (// perimeter_coll->first_point does not fit inside any slice
-                            i == n_slices ||
-                            // perimeter_coll->first_point fits inside ith slice
-                            point_inside_surface(i, perimeter_coll->first_point())) {
-                            if (islands[i].by_region.empty())
-                                islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
-                            islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities);
 
-                            // We just added perimeter_coll->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
-                            // we will note their indices (for each copy separately):
-                            unsigned int first_added_entity_index = islands[i].by_region[region_id].perimeters.entities.size() - perimeter_coll->entities.size();
-                            for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) {
-                                if (islands[i].by_region[region_id].perimeters_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
-                                    islands[i].by_region[region_id].perimeters_per_copy_ids.push_back(std::vector<unsigned int>());
-                                if (!perimeter_coll->is_extruder_overridden(copy_id))
-                                    for (int j=first_added_entity_index; j<islands[i].by_region[region_id].perimeters.entities.size(); ++j)
-                                        islands[i].by_region[region_id].perimeters_per_copy_ids[copy_id].push_back(j);
+                // Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
+                // It is also necessary to save which extrusions are part of MM wiping and which are not.
+                // The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
+                for (std::string entity_type("infills") ; entity_type != "done" ; entity_type = entity_type=="infills" ? "perimeters" : "done") {
+
+                    const ExtrusionEntitiesPtr& source_entities = entity_type=="infills" ? layerm->fills.entities : layerm->perimeters.entities;
+
+                    for (const ExtrusionEntity *ee : source_entities) {
+                        // fill represents infill extrusions of a single island.
+                        const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+                        if (fill->entities.empty()) // This shouldn't happen but first_point() would fail.
+                            continue;
+
+                        // This extrusion is part of certain Region, which tells us which extruder should be used for it:
+                        int correct_extruder_id = entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+                                                                           std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+
+                        // Let's recover vector of extruder overrides:
+                        const ExtruderPerCopy* entity_overrides = const_cast<ToolOrdering::LayerTools&>(layer_tools).wiping_extrusions.get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
+
+                        // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
+                        for (unsigned int extruder : layer_tools.extruders)
+                        {
+                            // Init by_extruder item only if we actually use the extruder:
+                            if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() ||   // at least one copy is overridden to use this extruder
+                                std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end())  // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
+                            {
+                                std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
+                                    by_extruder,
+                                    extruder,
+                                    &layer_to_print - layers.data(),
+                                    layers.size(), n_slices+1);
+                                for (size_t i = 0; i <= n_slices; ++i)
+                                    if (// fill->first_point does not fit inside any slice
+                                        i == n_slices ||
+                                        // fill->first_point fits inside ith slice
+                                        point_inside_surface(i, fill->first_point())) {
+                                        if (islands[i].by_region.empty())
+                                            islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
+                                        islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size());
+                                        break;
+                                    }
                             }
-                            break;
-                        }
-                }
-
-                // process infill
-                // layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection), 
-                // each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface"
-                // throughout the code). We can redefine the order of such Collections but we have to 
-                // do each one completely at once.
-                for (const ExtrusionEntity *ee : layerm->fills.entities) {
-                    // fill represents infill extrusions of a single island.
-                    const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
-                    if (fill->entities.empty())
-                        // This shouldn't happen but first_point() would fail.
-                        continue;
-
-                    // init by_extruder item only if we actually use the extruder
-                    int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1);
-                    // Init by_extruder item only if we actually use the extruder.
-                    std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
-                        by_extruder,
-                        extruder_id,
-                        &layer_to_print - layers.data(),
-                        layers.size(), n_slices+1);
-                    for (size_t i = 0; i <= n_slices; ++i)
-                        if (// fill->first_point does not fit inside any slice
-                            i == n_slices ||
-                            // fill->first_point fits inside ith slice
-                            point_inside_surface(i, fill->first_point())) {
-                            if (islands[i].by_region.empty())
-                                islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
-                            islands[i].by_region[region_id].infills.append(fill->entities);
-
-                            // We just added fill->entities.size() entities, if they are not to be printed before the main object (during infill wiping),
-                            // we will note their indices (for each copy separately):
-                            unsigned int first_added_entity_index = islands[i].by_region[region_id].infills.entities.size() - fill->entities.size();
-                            for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) {
-                                if (islands[i].by_region[region_id].infills_per_copy_ids.size() < copy_id + 1)  // if this copy isn't in the list yet
-                                    islands[i].by_region[region_id].infills_per_copy_ids.push_back(std::vector<unsigned int>());
-                                if (!fill->is_extruder_overridden(copy_id))
-                                    for (int j=first_added_entity_index; j<islands[i].by_region[region_id].infills.entities.size(); ++j)
-                                        islands[i].by_region[region_id].infills_per_copy_ids[copy_id].push_back(j);
-                            }
-                            break;
                         }
+                    }
                 }
             } // for regions
         }
     } // for objects
 
+
+
     // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
     std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
     for (unsigned int extruder_id : layer_tools.extruders)
-    {   
+    {
         gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
             m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
             this->set_extruder(extruder_id);
@@ -1335,7 +1305,7 @@ void GCode::process_layer(
                     for (ExtrusionPath &path : loop.paths) {
                         path.height     = (float)layer.height;
                         path.mm3_per_mm = mm3_per_mm;
-                    }                
+                    }
                     gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
                 }
                 m_avoid_crossing_perimeters.use_external_mp = false;
@@ -1344,7 +1314,7 @@ void GCode::process_layer(
                     m_avoid_crossing_perimeters.disable_once = true;
             }
         }
-        
+
         // Extrude brim with the extruder of the 1st region.
         if (! m_brim_done) {
             this->set_origin(0., 0.);
@@ -1357,100 +1327,59 @@ void GCode::process_layer(
             m_avoid_crossing_perimeters.disable_once = true;
         }
 
-        if (layer_tools.has_wipe_tower)   // the infill/perimeter wiping to save the material on the wipe tower
-        {
-            gcode += "; INFILL WIPING STARTS\n";
-            if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange
-                for (const auto& layer_to_print : layers) {     // iterate through all objects
-                    if (layer_to_print.object_layer == nullptr)
-                        continue;
-
-                    m_config.apply((layer_to_print.object_layer)->object()->config, true);
-
-                    for (unsigned copy_id = 0; copy_id < layer_to_print.object()->copies().size(); ++copy_id) {
-                        std::vector<ObjectByExtruder::Island::Region> overridden;
-                        for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
-                            ObjectByExtruder::Island::Region new_region;
-                            overridden.push_back(new_region);
-                            for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) {
-                                auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                if (fill->get_extruder_override(copy_id) == (int)extruder_id)
-                                        overridden.back().infills.append(*fill);
-                           }
-                           for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->perimeters.entities) {
-                                auto *fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                if (fill->get_extruder_override(copy_id) == (int)extruder_id)
-                                        overridden.back().perimeters.append((*fill).entities);
-                           }
-                        }
-
-                        Point copy = (layer_to_print.object_layer)->object()->_shifted_copies[copy_id];
-                        this->set_origin(unscale(copy.x), unscale(copy.y));
-
-
-                        std::unique_ptr<EdgeGrid::Grid> u;
-                        if (print.config.infill_first) {
-                            gcode += this->extrude_infill(print, overridden);
-                            gcode += this->extrude_perimeters(print, overridden, u);
-                        }
-                        else {
-                            gcode += this->extrude_perimeters(print, overridden, u);
-                            gcode += this->extrude_infill(print, overridden);
-                        }
-                    }
-                }
-            }
-            gcode += "; WIPING FINISHED\n";
-        }
-
-
 
         auto objects_by_extruder_it = by_extruder.find(extruder_id);
         if (objects_by_extruder_it == by_extruder.end())
             continue;
-        for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
-            const size_t       layer_id     = &object_by_extruder - objects_by_extruder_it->second.data();
-            const PrintObject *print_object = layers[layer_id].object();
-            if (print_object == nullptr)
-                // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
-                continue;
 
-            m_config.apply(print_object->config, true);
-            m_layer = layers[layer_id].layer();
-            if (m_config.avoid_crossing_perimeters)
-                m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
-            Points copies;
-            if (single_object_idx == size_t(-1)) 
-                copies = print_object->_shifted_copies;
-            else
-                copies.push_back(print_object->_shifted_copies[single_object_idx]);
-            // Sort the copies by the closest point starting with the current print position.
+        // We are almost ready to print. However, we must go through all the object twice and only print the overridden extrusions first (infill/primeter wiping feature):
+        for (int print_wipe_extrusions=layer_tools.wiping_extrusions.is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
+            for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
+                const size_t       layer_id     = &object_by_extruder - objects_by_extruder_it->second.data();
+                const PrintObject *print_object = layers[layer_id].object();
+                if (print_object == nullptr)
+                    // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
+                    continue;
 
-            unsigned int copy_id = 0;
-            for (const Point &copy : copies) {
-                // When starting a new object, use the external motion planner for the first travel move.
-                std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
-                if (m_last_obj_copy != this_object_copy)
-                    m_avoid_crossing_perimeters.use_external_mp_once = true;
-                m_last_obj_copy = this_object_copy;
-                this->set_origin(unscale(copy.x), unscale(copy.y));
-                if (object_by_extruder.support != nullptr) {
-                    m_layer = layers[layer_id].support_layer;
-                    gcode += this->extrude_support(
-                        // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
-                        object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
-                    m_layer = layers[layer_id].layer();
-                }
-                for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
-                    if (print.config.infill_first) {
-                        gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
-                        gcode += this->extrude_perimeters(print, island.by_region_per_copy(copy_id), lower_layer_edge_grids[layer_id]);
-                    } else {
-                        gcode += this->extrude_perimeters(print, island.by_region_per_copy(copy_id), lower_layer_edge_grids[layer_id]);
-                        gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id));
+                m_config.apply(print_object->config, true);
+                m_layer = layers[layer_id].layer();
+                if (m_config.avoid_crossing_perimeters)
+                    m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
+                Points copies;
+                if (single_object_idx == size_t(-1))
+                    copies = print_object->_shifted_copies;
+                else
+                    copies.push_back(print_object->_shifted_copies[single_object_idx]);
+                // Sort the copies by the closest point starting with the current print position.
+
+                unsigned int copy_id = 0;
+                for (const Point &copy : copies) {
+                    // When starting a new object, use the external motion planner for the first travel move.
+                    std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
+                    if (m_last_obj_copy != this_object_copy)
+                        m_avoid_crossing_perimeters.use_external_mp_once = true;
+                    m_last_obj_copy = this_object_copy;
+                    this->set_origin(unscale(copy.x), unscale(copy.y));
+                    if (object_by_extruder.support != nullptr) {
+                        m_layer = layers[layer_id].support_layer;
+                        gcode += this->extrude_support(
+                            // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
+                            object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
+                        m_layer = layers[layer_id].layer();
                     }
+                    for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
+                        const auto& by_region_specific = layer_tools.wiping_extrusions.is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
+
+                        if (print.config.infill_first) {
+                            gcode += this->extrude_infill(print, by_region_specific);
+                            gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+                        } else {
+                            gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+                            gcode += this->extrude_infill(print,by_region_specific);
+                        }
+                    }
+                    ++copy_id;
                 }
-                ++copy_id;
             }
         }
     }
@@ -2512,29 +2441,61 @@ Point GCode::gcode_to_point(const Pointf &point) const
 }
 
 
-// Goes through by_region std::vector and returns reference to a subvector of entities to be printed in usual time
-// i.e. not when it's going to be done during infill wiping
-const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy)
+// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
+// during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
+// Returns a reference to member to avoid copying.
+const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities)
 {
-    if (copy == last_copy)
-        return by_region_per_copy_cache;
-    else {
-        by_region_per_copy_cache.clear();
-        last_copy = copy;
-    }
+    by_region_per_copy_cache.clear();
 
     for (const auto& reg : by_region) {
-        by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region());
+        by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island
 
-        if (!reg.infills_per_copy_ids.empty())
-            for (unsigned int i=0; i<reg.infills_per_copy_ids[copy].size(); ++i)
-                by_region_per_copy_cache.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
+        // Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
+        // References are used so that we don't have to repeat the same code
+        for (int iter = 0; iter < 2; ++iter) {
+            const ExtrusionEntitiesPtr&         entities     = (iter ? reg.infills.entities : reg.perimeters.entities);
+            ExtrusionEntityCollection&          target_eec   = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
+            const std::vector<const ExtruderPerCopy*>& overrides   = (iter ? reg.infills_overrides : reg.perimeters_overrides);
 
-        if (!reg.perimeters_per_copy_ids.empty())
-            for (unsigned int i=0; i<reg.perimeters_per_copy_ids[copy].size(); ++i)
-                by_region_per_copy_cache.back().perimeters.append(*(reg.perimeters.entities[reg.perimeters_per_copy_ids[copy][i]]));
+            // Now the most important thing - which extrusion should we print.
+            // See function ToolOrdering::get_extruder_overrides for details about the negative numbers hack.
+            int this_extruder_mark = wiping_entities ? extruder : -extruder-1;
+
+            for (unsigned int i=0;i<entities.size();++i)
+                if (overrides[i]->at(copy) == this_extruder_mark)   // this copy should be printed with this extruder
+                    target_eec.append((*entities[i]));
+        }
     }
     return by_region_per_copy_cache;
 }
 
+
+
+// This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter)
+// It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy.
+void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num)
+{
+    // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
+    ExtrusionEntityCollection* perimeters_or_infills = &infills;
+    std::vector<const ExtruderPerCopy*>* perimeters_or_infills_overrides = &infills_overrides;
+
+    if (type == "perimeters") {
+        perimeters_or_infills = &perimeters;
+        perimeters_or_infills_overrides    = &perimeters_overrides;
+    }
+    else
+        if (type != "infills") {
+            CONFESS("Unknown parameter!");
+            return;
+        }
+
+
+    // First we append the entities, there are eec->entities.size() of them:
+    perimeters_or_infills->append(eec->entities);
+
+    for (unsigned int i=0;i<eec->entities.size();++i)
+        perimeters_or_infills_overrides->push_back(copies_extruder);
+}
+
 }   // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 35f80b578..ad3f1e26b 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -200,6 +200,7 @@ protected:
     std::string     extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
     std::string     extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
 
+    typedef std::vector<int> ExtruderPerCopy;
     // Extruding multiple objects with soluble / non-soluble / combined supports
     // on a multi-material printer, trying to minimize tool switches.
     // Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
@@ -215,15 +216,19 @@ protected:
             struct Region {
                 ExtrusionEntityCollection perimeters;
                 ExtrusionEntityCollection infills;
-                std::vector<std::vector<unsigned int>> infills_per_copy_ids;    // indices of infill.entities that are not part of infill wiping (an element for each object copy)
-                std::vector<std::vector<unsigned int>> perimeters_per_copy_ids; // indices of infill.entities that are not part of infill wiping (an element for each object copy)
+
+                std::vector<const ExtruderPerCopy*> infills_overrides;
+                std::vector<const ExtruderPerCopy*> perimeters_overrides;
+
+                // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
+                void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num);
             };
+
             std::vector<Region> by_region;                                    // all extrusions for this island, grouped by regions
-            const std::vector<Region>& by_region_per_copy(unsigned int copy); // returns reference to subvector of by_region (only extrusions that are NOT printed during wiping into infill for this copy)
+            const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region
 
         private:
             std::vector<Region> by_region_per_copy_cache;   // caches vector generated by function above to avoid copying and recalculating
-            unsigned int last_copy = (unsigned int)(-1);    // index of last copy that by_region_per_copy was called for
         };
         std::vector<Island>         islands;
     };
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index e0aa2b1c5..d2532d72d 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -330,4 +330,42 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
     }
 }
 
+    // This function is called from Print::mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
+    void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies) {
+        something_overridden = true;
+
+        auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
+        auto& copies_vector = entity_map_it->second;
+        if (copies_vector.size() < num_of_copies)
+            copies_vector.resize(num_of_copies, -1);
+
+        if (copies_vector[copy_id] != -1)
+            std::cout << "ERROR: Entity extruder overriden multiple times!!!\n";    // A debugging message - this must never happen.
+
+        copies_vector[copy_id] = extruder;
+    }
+
+
+
+    // Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
+    // It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
+    // It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
+    // so -1 was used as "print as usual".
+    // The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
+    // its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
+    const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies) {
+        auto entity_map_it = entity_map.find(entity);
+        if (entity_map_it == entity_map.end())
+            entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
+
+        // Now the entity_map_it should be valid, let's make sure the vector is long enough:
+        entity_map_it->second.resize(num_of_copies, -1);
+
+        // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
+        std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
+
+        return &(entity_map_it->second);
+    }
+    
+
 } // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index c92806b19..6dbb9715c 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -10,6 +10,36 @@ namespace Slic3r {
 class Print;
 class PrintObject;
 
+
+
+// Object of this class holds information about whether an extrusion is printed immediately
+// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
+// of several copies - this has to be taken into account.
+class WipingExtrusions
+{
+    public:
+    bool is_anything_overridden() const {   // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
+        return something_overridden;
+    }
+
+    // Returns true in case that entity is not printed with its usual extruder for a given copy:
+    bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const {
+        return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
+    }
+
+    // This function is called from Print::mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
+    void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
+
+    // This is called from GCode::process_layer - see implementation for further comments:
+    const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
+
+private:
+    std::map<const ExtrusionEntity*, std::vector<int>> entity_map;  // to keep track of who prints what
+    bool something_overridden = false;
+};
+
+
+
 class ToolOrdering 
 {
 public:
@@ -39,6 +69,11 @@ public:
 		// and to support the wipe tower partitions above this one.
 	    size_t                      wipe_tower_partitions;
 	    coordf_t 					wipe_tower_layer_height;
+
+
+        // This holds list of extrusion that will be used for extruder wiping
+        WipingExtrusions wiping_extrusions;
+
 	};
 
 	ToolOrdering() {}
@@ -72,7 +107,7 @@ public:
 	std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
 	std::vector<LayerTools>::const_iterator end()   const { return m_layer_tools.end(); }
 	bool 				empty()       const { return m_layer_tools.empty(); }
-	const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
+	std::vector<LayerTools>& 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; }
 
 private:
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index d448ab2f3..1b0627f78 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1121,21 +1121,14 @@ void Print::_make_wipe_tower()
             this->config.filament_ramming_parameters.get_at(i),
             this->config.nozzle_diameter.get_at(i));
 
-    // When printing the first layer's wipe tower, the first extruder is expected to be active and primed.
-    // Therefore the number of wipe sections at the wipe tower will be (m_tool_ordering.front().extruders-1) at the 1st layer.
-    // The following variable is true if the last priming section cannot be squeezed inside the wipe tower.
-    bool last_priming_wipe_full = m_tool_ordering.front().extruders.size() > m_tool_ordering.front().wipe_tower_partitions;
-
     m_wipe_tower_priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
-        wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full));
-
-    reset_wiping_extrusions(); // if this is not the first time the wipe tower is generated, some extrusions might remember their last wiping status
+        wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), false));
 
     // Lets go through the wipe tower layers and determine pairs of extruder changes for each
     // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
     {
         unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
-        for (const auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
+        for (auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
             if (!layer_tools.has_wipe_tower) continue;
             bool first_layer = &layer_tools == &m_tool_ordering.front();
             wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
@@ -1180,111 +1173,100 @@ void Print::_make_wipe_tower()
 }
 
 
-
-void Print::reset_wiping_extrusions() {
-     for (size_t i = 0; i < objects.size(); ++ i) {
-        for (auto& this_layer : objects[i]->layers) {
-            for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
-                for (unsigned int copy = 0; copy < objects[i]->_shifted_copies.size(); ++copy) {
-                    this_layer->regions[region_id]->fills.set_extruder_override(copy, -1);
-                    this_layer->regions[region_id]->perimeters.set_extruder_override(copy, -1);
-                }
-            }
-        }
-     }
-}
-
-
-
-// Strategy for  wiping (TODO):
-// if !infill_first
-//      start with dedicated objects
-//            print a perimeter and its corresponding infill immediately after
-//            repeat until there are no dedicated objects left
-//            if there are some left and this is the last toolchange on the layer, mark all remaining extrusions of the object (so we don't have to travel back to it later)
-//      move to normal objects
-//            start with one object and start assigning its infill, if their perimeters ARE ALREADY EXTRUDED
-//            never touch perimeters
-//
-// if infill first
-//        start with dedicated objects
-//            print an infill and its corresponding perimeter immediately after
-//            repeat until you run out of infills
-//        move to normal objects
-//            start assigning infills (one copy after another)
-//            repeat until you run out of infills, leave perimeters be
-
-
-float Print::mark_wiping_extrusions(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
+// 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 Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
 {
+    // Strategy for  wiping (TODO):
+    // if !infill_first
+    //      start with dedicated objects
+    //            print a perimeter and its corresponding infill immediately after
+    //            repeat until there are no dedicated objects left
+    //            if there are some left and this is the last toolchange on the layer, mark all remaining extrusions of the object (so we don't have to travel back to it later)
+    //      move to normal objects
+    //            start with one object and start assigning its infill, if their perimeters ARE ALREADY EXTRUDED
+    //            never touch perimeters
+    //
+    // if infill first
+    //        start with dedicated objects
+    //            print an infill and its corresponding perimeter immediately after
+    //            repeat until you run out of infills
+    //        move to normal objects
+    //            start assigning infills (one copy after another)
+    //            repeat until you run out of infills, leave perimeters be
+
     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 
-    if (!config.filament_soluble.get_at(new_extruder)) {                            // Soluble filament cannot be wiped in a random infill
-        for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
+    if (config.filament_soluble.get_at(new_extruder))
+        return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
 
-            if (!objects[i]->config.wipe_into_infill && !objects[i]->config.wipe_into_objects)
-                continue;
 
-            Layer* this_layer = nullptr;
-            for (unsigned int a = 0; a < objects[i]->layers.size(); ++a) // Finds this layer
-                if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON) {
-                    this_layer = objects[i]->layers[a];
-                    break;
-                }
-            if (this_layer == nullptr)
-                continue;
 
-            for (unsigned int copy = 0; copy < objects[i]->_shifted_copies.size(); ++copy) {    // iterate through copies first, so that we mark neighbouring infills
-                for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
+    for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
+        if (!objects[i]->config.wipe_into_infill && !objects[i]->config.wipe_into_objects)
+            continue;
 
-                    unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
-                    if (config.filament_soluble.get_at(region_extruder)) // if this infill is meant to be soluble, keep it that way
+        Layer* this_layer = nullptr;
+        for (unsigned int a = 0; a < objects[i]->layers.size(); ++a) // Finds this layer
+            if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON) {
+                this_layer = objects[i]->layers[a];
+                break;
+            }
+        if (this_layer == nullptr)
+            continue;
+
+        unsigned int num_of_copies = objects[i]->_shifted_copies.size();
+
+        for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+
+            for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
+                unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
+                if (config.filament_soluble.get_at(region_extruder)) // if this entity is meant to be soluble, keep it that way
+                    continue;
+
+                if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded)
+                    bool unused_yet = false;
+                    for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
+                        if (layer_tools.extruders[i] == new_extruder)
+                            unused_yet = true;
+                        if (layer_tools.extruders[i] == region_extruder)
+                            break;
+                    }
+                    if (unused_yet)
                         continue;
+                }
 
-                    if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded)
-                        bool unused_yet = false;
-                        for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
-                            if (layer_tools.extruders[i] == new_extruder)
-                                unused_yet = true;
-                            if (layer_tools.extruders[i] == region_extruder)
+                if (objects[i]->config.wipe_into_infill) {
+                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
+                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
+                        if (volume_to_wipe <= 0.f)
                                 break;
-                        }
-                        if (unused_yet)
+                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
                             continue;
-                    }
-
-                    if (objects[i]->config.wipe_into_infill) {
-                        ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
-                        for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
-                            auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                            if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue;         // these cannot be changed - it is / may be visible
-                                if (volume_to_wipe <= 0.f)
-                                    break;
-                                if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
-                                    fill->set_extruder_override(copy, new_extruder);
-                                    volume_to_wipe -= fill->total_volume();
-                                }
+                        if (/*!fill->is_extruder_overridden(copy)*/ !layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+                            layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies);
+                            volume_to_wipe -= fill->total_volume();
                         }
                     }
+                }
 
-                    if (objects[i]->config.wipe_into_objects)
-                    {
-                        ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
-                        for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
-                            auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                                if (volume_to_wipe <= 0.f)
-                                    break;
-                                if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) {
-                                    fill->set_extruder_override(copy, new_extruder);
-                                    volume_to_wipe -= fill->total_volume();
-                                }
+                if (objects[i]->config.wipe_into_objects)
+                {
+                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
+                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
+                        if (volume_to_wipe <= 0.f)
+                            break;
+                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
+                        if (/*!fill->is_extruder_overridden(copy)*/ !layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
+                            layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies);
+                            volume_to_wipe -= fill->total_volume();
                         }
                     }
                 }
             }
         }
     }
-
     return std::max(0.f, volume_to_wipe);
 }
 
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 77787063e..57b1f4015 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -317,10 +317,7 @@ private:
 
     // 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 ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
-
-    // A function to go through all entities and unsets their extruder_override flag
-    void reset_wiping_extrusions();
+    float mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
 
     // Has the calculation been canceled?
     tbb::atomic<bool>   m_canceled;

From bc5bd1b42b019d9ff526efaf199dd30034591c44 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 21 Jun 2018 10:16:52 +0200
Subject: [PATCH 22/36] Assigning of wiping extrusions improved

---
 xs/src/libslic3r/GCode.cpp              |   2 +-
 xs/src/libslic3r/GCode/ToolOrdering.cpp |  56 +++++++------
 xs/src/libslic3r/GCode/ToolOrdering.hpp |   1 -
 xs/src/libslic3r/Print.cpp              | 106 +++++++++++++-----------
 xs/src/libslic3r/Print.hpp              |  10 +++
 xs/src/libslic3r/PrintConfig.hpp        |   4 +-
 6 files changed, 101 insertions(+), 78 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index cd27e3edd..b06232a92 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1239,7 +1239,7 @@ void GCode::process_layer(
                             continue;
 
                         // This extrusion is part of certain Region, which tells us which extruder should be used for it:
-                        int correct_extruder_id = entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+                        int correct_extruder_id = get_extruder(fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
                                                                            std::max<int>(region.config.perimeter_extruder.value - 1, 0);
 
                         // Let's recover vector of extruder overrides:
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index d2532d72d..719f7a97a 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -330,42 +330,44 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
     }
 }
 
-    // This function is called from Print::mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
-    void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies) {
-        something_overridden = true;
 
-        auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
-        auto& copies_vector = entity_map_it->second;
-        if (copies_vector.size() < num_of_copies)
-            copies_vector.resize(num_of_copies, -1);
 
-        if (copies_vector[copy_id] != -1)
-            std::cout << "ERROR: Entity extruder overriden multiple times!!!\n";    // A debugging message - this must never happen.
+// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
+void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies) {
+    something_overridden = true;
 
-        copies_vector[copy_id] = extruder;
-    }
+    auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
+    auto& copies_vector = entity_map_it->second;
+    if (copies_vector.size() < num_of_copies)
+        copies_vector.resize(num_of_copies, -1);
+
+    if (copies_vector[copy_id] != -1)
+        std::cout << "ERROR: Entity extruder overriden multiple times!!!\n";    // A debugging message - this must never happen.
+
+    copies_vector[copy_id] = extruder;
+}
 
 
 
-    // Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
-    // It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
-    // It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
-    // so -1 was used as "print as usual".
-    // The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
-    // its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
-    const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies) {
-        auto entity_map_it = entity_map.find(entity);
-        if (entity_map_it == entity_map.end())
-            entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
+// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
+// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
+// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
+// so -1 was used as "print as usual".
+// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
+// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
+const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies) {
+    auto entity_map_it = entity_map.find(entity);
+    if (entity_map_it == entity_map.end())
+        entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
 
-        // Now the entity_map_it should be valid, let's make sure the vector is long enough:
-        entity_map_it->second.resize(num_of_copies, -1);
+    // Now the entity_map_it should be valid, let's make sure the vector is long enough:
+    entity_map_it->second.resize(num_of_copies, -1);
 
-        // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
-        std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
+    // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
+    std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
 
-        return &(entity_map_it->second);
-    }
+    return &(entity_map_it->second);
+}
     
 
 } // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index 6dbb9715c..241567a75 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -11,7 +11,6 @@ class Print;
 class PrintObject;
 
 
-
 // Object of this class holds information about whether an extrusion is printed immediately
 // after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
 // of several copies - this has to be taken into account.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 1b0627f78..6749babf8 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1177,66 +1177,50 @@ void Print::_make_wipe_tower()
 // and returns volume that is left to be wiped on the wipe tower.
 float Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
 {
-    // Strategy for  wiping (TODO):
-    // if !infill_first
-    //      start with dedicated objects
-    //            print a perimeter and its corresponding infill immediately after
-    //            repeat until there are no dedicated objects left
-    //            if there are some left and this is the last toolchange on the layer, mark all remaining extrusions of the object (so we don't have to travel back to it later)
-    //      move to normal objects
-    //            start with one object and start assigning its infill, if their perimeters ARE ALREADY EXTRUDED
-    //            never touch perimeters
-    //
-    // if infill first
-    //        start with dedicated objects
-    //            print an infill and its corresponding perimeter immediately after
-    //            repeat until you run out of infills
-    //        move to normal objects
-    //            start assigning infills (one copy after another)
-    //            repeat until you run out of infills, leave perimeters be
-
     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 
     if (config.filament_soluble.get_at(new_extruder))
         return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
 
+    PrintObjectPtrs object_list = objects;
+
+    // sort objects so that dedicated for wiping are at the beginning:
+    std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; }); 
 
 
-    for (size_t i = 0; i < objects.size(); ++ i) {                              // Let's iterate through all objects...
-        if (!objects[i]->config.wipe_into_infill && !objects[i]->config.wipe_into_objects)
+    // We will now iterate through objects
+    //  - first through the dedicated ones to mark perimeters or infills (depending on infill_first)
+    //  - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
+    //  - then for the others to mark infills
+    // this is controlled by the following variable:
+    bool perimeters_done = false;
+
+    for (int i=0 ; i<(int)object_list.size() ; ++i) {                              // Let's iterate through all objects...
+        const auto& object = object_list[i];
+
+        if (!perimeters_done && (i+1==objects.size() || !objects[i+1]->config.wipe_into_objects)) { // last dedicated object in list
+            perimeters_done = true;
+            i=-1;   // let's go from the start again
             continue;
+        }
 
-        Layer* this_layer = nullptr;
-        for (unsigned int a = 0; a < objects[i]->layers.size(); ++a) // Finds this layer
-            if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON) {
-                this_layer = objects[i]->layers[a];
-                break;
-            }
-        if (this_layer == nullptr)
+        // Finds this layer:
+        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; });
+        if (this_layer_it == object->layers.end())
             continue;
-
-        unsigned int num_of_copies = objects[i]->_shifted_copies.size();
+        const Layer* this_layer = *this_layer_it;
+        unsigned int num_of_copies = object->_shifted_copies.size();
 
         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 
-            for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) {
-                unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based
-                if (config.filament_soluble.get_at(region_extruder)) // if this entity is meant to be soluble, keep it that way
+            for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
+                const auto& region = *object->print()->regions[region_id];
+
+                if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
                     continue;
 
-                if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded)
-                    bool unused_yet = false;
-                    for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
-                        if (layer_tools.extruders[i] == new_extruder)
-                            unused_yet = true;
-                        if (layer_tools.extruders[i] == region_extruder)
-                            break;
-                    }
-                    if (unused_yet)
-                        continue;
-                }
 
-                if (objects[i]->config.wipe_into_infill) {
+                if (((!config.infill_first ? perimeters_done : !perimeters_done) || !object->config.wipe_into_objects) && region.config.wipe_into_infill) {
                     ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
                     for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
                         if (volume_to_wipe <= 0.f)
@@ -1244,21 +1228,49 @@ float Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsig
                         auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
                         if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
                             continue;
-                        if (/*!fill->is_extruder_overridden(copy)*/ !layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
+
+                        // What extruder would this normally be printed with?
+                        unsigned int correct_extruder = get_extruder(fill, region);
+                        if (config.filament_soluble.get_at(correct_extruder)) // if this entity is meant to be soluble, keep it that way
+                            continue;
+
+                        if (!object->config.wipe_into_objects && !config.infill_first)  {
+                            // In this case we must check that the original extruder is used on this layer before the one we are overridding
+                            // (and the perimeters will be finished before the infill is printed):
+                            if (!config.infill_first && region.config.wipe_into_infill) {
+                                bool unused_yet = false;
+                                for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
+                                    if (layer_tools.extruders[i] == new_extruder)
+                                        unused_yet = true;
+                                    if (layer_tools.extruders[i] == correct_extruder)
+                                        break;
+                                }
+                                if (unused_yet)
+                                    continue;
+                            }
+                        }
+
+                        if (!layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
                             layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies);
                             volume_to_wipe -= fill->total_volume();
                         }
                     }
                 }
 
-                if (objects[i]->config.wipe_into_objects)
+
+                if ((config.infill_first ? perimeters_done : !perimeters_done) && object->config.wipe_into_objects)
                 {
                     ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
                     for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
                         if (volume_to_wipe <= 0.f)
                             break;
                         auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                        if (/*!fill->is_extruder_overridden(copy)*/ !layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
+                        // What extruder would this normally be printed with?
+                        unsigned int correct_extruder = get_extruder(fill, region);
+                        if (config.filament_soluble.get_at(correct_extruder)) // if this entity is meant to be soluble, keep it that way
+                            continue;
+
+                        if (!layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
                             layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies);
                             volume_to_wipe -= fill->total_volume();
                         }
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 57b1f4015..8d5e07970 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -24,6 +24,7 @@ class Print;
 class PrintObject;
 class ModelObject;
 
+
 // Print step IDs for keeping track of the print state.
 enum PrintStep {
     psSkirt, psBrim, psWipeTower, psCount,
@@ -323,6 +324,15 @@ private:
     tbb::atomic<bool>   m_canceled;
 };
 
+
+// Returns extruder this eec should be printed with, according to PrintRegion config
+static int get_extruder(const ExtrusionEntityCollection* fill, const PrintRegion &region) {
+    return is_infill(fill->role()) ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+                                     std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+}
+
+
+
 #define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
 #define FOREACH_REGION(print, region)       FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
 #define FOREACH_OBJECT(print, object)       FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 92ead2927..37d9357b2 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -337,7 +337,6 @@ public:
     ConfigOptionFloatOrPercent      support_material_xy_spacing;
     ConfigOptionFloat               xy_size_compensation;
     ConfigOptionBool                wipe_into_objects;
-    ConfigOptionBool                wipe_into_infill;
 
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
@@ -375,7 +374,6 @@ protected:
         OPT_PTR(support_material_with_sheath);
         OPT_PTR(xy_size_compensation);
         OPT_PTR(wipe_into_objects);
-        OPT_PTR(wipe_into_infill);
     }
 };
 
@@ -418,6 +416,7 @@ public:
     ConfigOptionFloatOrPercent      top_infill_extrusion_width;
     ConfigOptionInt                 top_solid_layers;
     ConfigOptionFloatOrPercent      top_solid_infill_speed;
+    ConfigOptionBool                wipe_into_infill;
     
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
@@ -456,6 +455,7 @@ protected:
         OPT_PTR(top_infill_extrusion_width);
         OPT_PTR(top_solid_infill_speed);
         OPT_PTR(top_solid_layers);
+        OPT_PTR(wipe_into_infill);
     }
 };
 

From e2126c2dd623048f74e9195c921926e33fbf03c7 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 22 Jun 2018 14:03:34 +0200
Subject: [PATCH 23/36] Dedicated objects are now not ignored

---
 xs/src/libslic3r/Print.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 6749babf8..dac48bfd3 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1198,7 +1198,7 @@ float Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsig
     for (int i=0 ; i<(int)object_list.size() ; ++i) {                              // Let's iterate through all objects...
         const auto& object = object_list[i];
 
-        if (!perimeters_done && (i+1==objects.size() || !objects[i+1]->config.wipe_into_objects)) { // last dedicated object in list
+        if (!perimeters_done && (i+1==object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
             perimeters_done = true;
             i=-1;   // let's go from the start again
             continue;
@@ -1224,7 +1224,7 @@ float Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsig
                     ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
                     for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
                         if (volume_to_wipe <= 0.f)
-                                break;
+                                return 0.f;
                         auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
                         if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
                             continue;
@@ -1258,12 +1258,12 @@ float Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsig
                 }
 
 
-                if ((config.infill_first ? perimeters_done : !perimeters_done) && object->config.wipe_into_objects)
+                if (object->config.wipe_into_objects && (config.infill_first ? perimeters_done : !perimeters_done))
                 {
                     ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
                     for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
                         if (volume_to_wipe <= 0.f)
-                            break;
+                            return 0.f;
                         auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
                         // What extruder would this normally be printed with?
                         unsigned int correct_extruder = get_extruder(fill, region);

From f8388abe17f8fbb119cc8d60aac4fa047e454952 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 26 Jun 2018 14:12:25 +0200
Subject: [PATCH 24/36] 'Dontcare' extrusions now don't force a toolchange +
 code reorganization

---
 xs/src/libslic3r/GCode.cpp              |  14 +-
 xs/src/libslic3r/GCode.hpp              |   2 +-
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 194 ++++++++++++++++++++++--
 xs/src/libslic3r/GCode/ToolOrdering.hpp | 106 +++++++------
 xs/src/libslic3r/Print.cpp              | 113 +-------------
 xs/src/libslic3r/Print.hpp              |  10 +-
 6 files changed, 259 insertions(+), 180 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index b06232a92..1271ee9ee 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -764,7 +764,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
         }
         // Extrude the layers.
         for (auto &layer : layers_to_print) {
-            const ToolOrdering::LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
+            const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
             if (m_wipe_tower && layer_tools.has_wipe_tower)
                 m_wipe_tower->next_layer();
             this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
@@ -1009,7 +1009,7 @@ void GCode::process_layer(
     const Print                     &print,
     // Set of object & print layers of the same PrintObject and with the same print_z.
     const std::vector<LayerToPrint> &layers,
-    const ToolOrdering::LayerTools  &layer_tools,
+    const LayerTools  &layer_tools,
     // If set to size_t(-1), then print all copies of all objects.
     // Otherwise print a single copy of a single object.
     const size_t                     single_object_idx)
@@ -1239,18 +1239,20 @@ void GCode::process_layer(
                             continue;
 
                         // This extrusion is part of certain Region, which tells us which extruder should be used for it:
-                        int correct_extruder_id = get_extruder(fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+                        int correct_extruder_id = get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
                                                                            std::max<int>(region.config.perimeter_extruder.value - 1, 0);
 
                         // Let's recover vector of extruder overrides:
-                        const ExtruderPerCopy* entity_overrides = const_cast<ToolOrdering::LayerTools&>(layer_tools).wiping_extrusions.get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
+                        const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions.get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
 
                         // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
                         for (unsigned int extruder : layer_tools.extruders)
                         {
                             // Init by_extruder item only if we actually use the extruder:
-                            if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() ||   // at least one copy is overridden to use this extruder
-                                std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end())  // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
+                            if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() ||      // at least one copy is overridden to use this extruder
+                                std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end() ||   // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
+                                (std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder_id) == layer_tools.extruders.end() && extruder == layer_tools.extruders.back())) // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
+                                                                                                                                            //by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
                             {
                                 std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
                                     by_extruder,
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index ad3f1e26b..a5c63f208 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -185,7 +185,7 @@ protected:
         const Print                     &print,
         // Set of object & print layers of the same PrintObject and with the same print_z.
         const std::vector<LayerToPrint> &layers,
-        const ToolOrdering::LayerTools  &layer_tools,
+        const LayerTools  &layer_tools,
         // If set to size_t(-1), then print all copies of all objects.
         // Otherwise print a single copy of a single object.
         const size_t                     single_object_idx = size_t(-1));
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 719f7a97a..34bb32e65 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -48,6 +48,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
 // (print.config.complete_objects is false).
 ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
 {
+    m_print_config_ptr = &print.config;
     // Initialize the print layers for all objects and all layers.
     coordf_t object_bottom_z = 0.;
     {
@@ -77,9 +78,9 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
 }
 
 
-ToolOrdering::LayerTools&  ToolOrdering::tools_for_layer(coordf_t print_z)
+LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
 {
-    auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), ToolOrdering::LayerTools(print_z - EPSILON));
+    auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
     assert(it_layer_tools != m_layer_tools.end());
     coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
 	for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) {
@@ -103,7 +104,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
         coordf_t zmax = zs[i] + EPSILON;
         for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
         // Assign an average print_z to the set of layers with nearly equal print_z.
-        m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1])));
+        m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr));
         i = j;
     }
 }
@@ -135,12 +136,25 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
             if (layerm == nullptr)
                 continue;
             const PrintRegion &region = *object.print()->regions[region_id];
+
             if (! layerm->perimeters.entities.empty()) {
-                layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
+                bool something_nonoverriddable = false;
+                for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
+                    if (!layer_tools.wiping_extrusions.is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
+                        something_nonoverriddable = true;
+                        break;
+                    }
+
+                if (something_nonoverriddable)
+                    layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
+
                 layer_tools.has_object = true;
             }
+
+
             bool has_infill       = false;
             bool has_solid_infill = false;
+            bool something_nonoverriddable = false;
             for (const ExtrusionEntity *ee : layerm->fills.entities) {
                 // fill represents infill extrusions of a single island.
                 const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
@@ -149,19 +163,32 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
                     has_solid_infill = true;
                 else if (role != erNone)
                     has_infill = true;
+
+                if (!something_nonoverriddable && !layer_tools.wiping_extrusions.is_overriddable(*fill, *m_print_config_ptr, object, region))
+                    something_nonoverriddable = true;
+            }
+            if (something_nonoverriddable)
+            {
+                if (has_solid_infill)
+                    layer_tools.extruders.push_back(region.config.solid_infill_extruder);
+                if (has_infill)
+                    layer_tools.extruders.push_back(region.config.infill_extruder);
             }
-            if (has_solid_infill)
-                layer_tools.extruders.push_back(region.config.solid_infill_extruder);
-            if (has_infill)
-                layer_tools.extruders.push_back(region.config.infill_extruder);
             if (has_solid_infill || has_infill)
                 layer_tools.has_object = true;
         }
     }
 
-    // Sort and remove duplicates
-    for (LayerTools &lt : m_layer_tools)
-        sort_remove_duplicates(lt.extruders);
+    // Sort and remove duplicates, make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
+    for (auto lt_it=m_layer_tools.begin(); lt_it != m_layer_tools.end(); ++lt_it) {
+        sort_remove_duplicates(lt_it->extruders);
+
+        if (lt_it->extruders.empty() && lt_it->has_object)
+            if (lt_it != m_layer_tools.begin())
+                lt_it->extruders.push_back(std::prev(lt_it)->extruders.back());
+            else
+                lt_it->extruders.push_back(1);
+    }
 }
 
 // Reorder extruders to minimize layer changes.
@@ -348,6 +375,151 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi
 }
 
 
+// Finds last non-soluble extruder on the layer
+bool WipingExtrusions::is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const {
+    for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
+        if (!print_config.filament_soluble.get_at(*extruders_it))
+            return (*extruders_it == extruder);
+    return false;
+}
+
+
+// Decides whether this entity could be overridden
+bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const {
+    if ((!is_infill(eec.role()) && !object.config.wipe_into_objects) ||
+        ((eec.role() == erTopSolidInfill || eec.role() == erGapFill) && !object.config.wipe_into_objects) ||
+        (is_infill(eec.role()) && !region.config.wipe_into_infill) ||
+        (print_config.filament_soluble.get_at(get_extruder(eec, region))) )
+            return false;
+
+    return true;
+}
+
+
+// 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, const LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
+{
+    const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
+
+    if (print.config.filament_soluble.get_at(new_extruder))
+        return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
+
+    bool last_nonsoluble = is_last_nonsoluble_on_layer(print.config, layer_tools, new_extruder);
+
+    // we will sort objects so that dedicated for wiping are at the beginning:
+    PrintObjectPtrs object_list = print.objects;
+    std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
+
+
+    // We will now iterate through
+    //  - first the dedicated objects to mark perimeters or infills (depending on infill_first)
+    //  - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
+    //  - then all the others to mark infills (in case that !infill_first, we must also check that the perimeter is finished already
+    // this is controlled by the following variable:
+    bool perimeters_done = false;
+
+    for (int i=0 ; i<(int)object_list.size() ; ++i) {
+        const auto& object = object_list[i];
+
+        if (!perimeters_done && (i+1==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
+            perimeters_done = true;
+            i=-1;   // let's go from the start again
+            continue;
+        }
+
+        // Finds this layer:
+        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; });
+        if (this_layer_it == object->layers.end())
+            continue;
+        const Layer* this_layer = *this_layer_it;
+        unsigned int num_of_copies = object->_shifted_copies.size();
+
+        for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+
+            for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
+                const auto& region = *object->print()->regions[region_id];
+
+                if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+                    continue;
+
+
+                if (((!print.config.infill_first ? perimeters_done : !perimeters_done) || !object->config.wipe_into_objects) && region.config.wipe_into_infill) {
+                    const ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
+                    for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
+                        auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
+                            continue;
+
+                        // What extruder would this normally be printed with?
+                        unsigned int correct_extruder = get_extruder(*fill, region);
+
+                        bool force_override = false;
+                        // If the extruder is not in layer tools - we MUST override it. This happens whenever all extrusions, that would normally
+                        // be printed with this extruder on this layer are "dont care" (part of infill/perimeter wiping):
+                        if (last_nonsoluble && std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder) == layer_tools.extruders.end())
+                            force_override = true;
+                        if (!force_override && volume_to_wipe<=0)
+                            continue;
+
+                        if (!is_overriddable(*fill, print.config, *object, region))
+                            continue;
+
+                        if (!object->config.wipe_into_objects && !print.config.infill_first && !force_override) {
+                            // In this case we must check that the original extruder is used on this layer before the one we are overridding
+                            // (and the perimeters will be finished before the infill is printed):
+                            if ((!print.config.infill_first && region.config.wipe_into_infill)) {
+                                bool unused_yet = false;
+                                for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
+                                    if (layer_tools.extruders[i] == new_extruder)
+                                        unused_yet = true;
+                                    if (layer_tools.extruders[i] == correct_extruder)
+                                        break;
+                                }
+                                if (unused_yet)
+                                    continue;
+                            }
+                        }
+
+                        if (force_override || (!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {     // this infill will be used to wipe this extruder
+                            set_extruder_override(fill, copy, new_extruder, num_of_copies);
+                            volume_to_wipe -= fill->total_volume();
+                        }
+                    }
+                }
+
+
+                if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
+                {
+                    const ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
+                    for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
+                        auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+                        // What extruder would this normally be printed with?
+                        unsigned int correct_extruder = get_extruder(*fill, region);
+                        bool force_override = false;
+                        if (last_nonsoluble && std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder) == layer_tools.extruders.end())
+                            force_override = true;
+                        if (!force_override && volume_to_wipe<=0)
+                            continue;
+
+                        if (!is_overriddable(*fill, print.config, *object, region))
+                            continue;
+
+                        if (force_override || (!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
+                            set_extruder_override(fill, copy, new_extruder, num_of_copies);
+                            volume_to_wipe -= fill->total_volume();
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return std::max(0.f, volume_to_wipe);
+}
+
+
+
 
 // Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
 // It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index 241567a75..862b58f67 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -9,6 +9,8 @@ namespace Slic3r {
 
 class Print;
 class PrintObject;
+class LayerTools;
+
 
 
 // Object of this class holds information about whether an extrusion is printed immediately
@@ -16,65 +18,74 @@ class PrintObject;
 // of several copies - this has to be taken into account.
 class WipingExtrusions
 {
-    public:
+public:
     bool is_anything_overridden() const {   // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
         return something_overridden;
     }
 
+    // This is called from GCode::process_layer - see implementation for further comments:
+    const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
+
+    // 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, const LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
+
+    bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
+
+private:
+    bool is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const;
+
+    // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
+    void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
+
     // Returns true in case that entity is not printed with its usual extruder for a given copy:
     bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const {
         return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
     }
 
-    // This function is called from Print::mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
-    void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
-
-    // This is called from GCode::process_layer - see implementation for further comments:
-    const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
-
-private:
     std::map<const ExtrusionEntity*, std::vector<int>> entity_map;  // to keep track of who prints what
     bool something_overridden = false;
 };
 
 
 
-class ToolOrdering 
+class LayerTools
 {
 public:
-	struct LayerTools
-	{
-	    LayerTools(const coordf_t z) :
-	    	print_z(z), 
-	    	has_object(false),
-			has_support(false),
-			has_wipe_tower(false),
-	    	wipe_tower_partitions(0),
-	    	wipe_tower_layer_height(0.) {}
+    LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) :
+        print_z(z),
+        has_object(false),
+        has_support(false),
+        has_wipe_tower(false),
+        wipe_tower_partitions(0),
+        wipe_tower_layer_height(0.) {}
 
-	    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 operator< (const LayerTools &rhs) const { return print_z - EPSILON <  rhs.print_z; }
+    bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
 
-		coordf_t 					print_z;
-		bool 						has_object;
-		bool						has_support;
-		// Zero based extruder IDs, ordered to minimize tool switches.
-		std::vector<unsigned int> 	extruders;
-		// 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.
-		bool 						has_wipe_tower;
-		// Number of wipe tower partitions to support the required number of tool switches
-		// and to support the wipe tower partitions above this one.
-	    size_t                      wipe_tower_partitions;
-	    coordf_t 					wipe_tower_layer_height;
+    coordf_t 					print_z;
+    bool 						has_object;
+    bool						has_support;
+    // Zero based extruder IDs, ordered to minimize tool switches.
+    std::vector<unsigned int> 	extruders;
+    // 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.
+    bool 						has_wipe_tower;
+    // Number of wipe tower partitions to support the required number of tool switches
+    // and to support the wipe tower partitions above this one.
+    size_t                      wipe_tower_partitions;
+    coordf_t 					wipe_tower_layer_height;
+
+    // This object holds list of extrusion that will be used for extruder wiping
+    WipingExtrusions wiping_extrusions;
+};
 
 
-        // This holds list of extrusion that will be used for extruder wiping
-        WipingExtrusions wiping_extrusions;
-
-	};
 
+class ToolOrdering
+{
+public:
 	ToolOrdering() {}
 
 	// For the use case when each object is printed separately
@@ -114,17 +125,22 @@ private:
 	void 				collect_extruders(const PrintObject &object);
 	void				reorder_extruders(unsigned int last_extruder_id);
 	void 				fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
-	void 				collect_extruder_statistics(bool prime_multi_material);
+    void 				collect_extruder_statistics(bool prime_multi_material);
 
-	std::vector<LayerTools> 	m_layer_tools;
-	// First printing extruder, including the multi-material priming sequence.
-	unsigned int 				m_first_printing_extruder = (unsigned int)-1;
-	// Final printing extruder.
-	unsigned int 				m_last_printing_extruder  = (unsigned int)-1;
-	// All extruders, which extrude some material over m_layer_tools.
-    std::vector<unsigned int> 	m_all_printing_extruders;
+    std::vector<LayerTools>    m_layer_tools;
+    // First printing extruder, including the multi-material priming sequence.
+    unsigned int               m_first_printing_extruder = (unsigned int)-1;
+    // Final printing extruder.
+    unsigned int               m_last_printing_extruder  = (unsigned int)-1;
+    // All extruders, which extrude some material over m_layer_tools.
+    std::vector<unsigned int>  m_all_printing_extruders;
+
+
+    const PrintConfig*         m_print_config_ptr = nullptr;
 };
 
+
+
 } // namespace SLic3r
 
 #endif /* slic3r_ToolOrdering_hpp_ */
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index dac48bfd3..fcbe74b85 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1064,7 +1064,7 @@ void Print::_make_wipe_tower()
         size_t idx_end   = m_tool_ordering.layer_tools().size();
         // Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
         for (size_t i = 0; i < idx_end; ++ i) {
-            const ToolOrdering::LayerTools &lt = m_tool_ordering.layer_tools()[i];
+            const LayerTools &lt = m_tool_ordering.layer_tools()[i];
             if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
                 idx_begin = i;
                 break;
@@ -1078,7 +1078,7 @@ void Print::_make_wipe_tower()
             for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
             // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
             for (size_t i = idx_begin; i < idx_end; ++ i) {
-                ToolOrdering::LayerTools &lt = const_cast<ToolOrdering::LayerTools&>(m_tool_ordering.layer_tools()[i]);
+                LayerTools &lt = const_cast<LayerTools&>(m_tool_ordering.layer_tools()[i]);
                 if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
                     break;
                 lt.has_support = true;
@@ -1137,7 +1137,7 @@ void Print::_make_wipe_tower()
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];    // total volume to wipe after this toolchange
 
                     if (!first_layer)   // unless we're on the first layer, try to assign some infills/objects for the wiping:
-                        volume_to_wipe = mark_wiping_extrusions(layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
+                        volume_to_wipe = layer_tools.wiping_extrusions.mark_wiping_extrusions(*this, layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
 
                     wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
                     current_extruder_id = extruder_id;
@@ -1173,114 +1173,7 @@ void Print::_make_wipe_tower()
 }
 
 
-// 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 Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
-{
-    const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 
-    if (config.filament_soluble.get_at(new_extruder))
-        return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
-
-    PrintObjectPtrs object_list = objects;
-
-    // sort objects so that dedicated for wiping are at the beginning:
-    std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; }); 
-
-
-    // We will now iterate through objects
-    //  - first through the dedicated ones to mark perimeters or infills (depending on infill_first)
-    //  - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
-    //  - then for the others to mark infills
-    // this is controlled by the following variable:
-    bool perimeters_done = false;
-
-    for (int i=0 ; i<(int)object_list.size() ; ++i) {                              // Let's iterate through all objects...
-        const auto& object = object_list[i];
-
-        if (!perimeters_done && (i+1==object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
-            perimeters_done = true;
-            i=-1;   // let's go from the start again
-            continue;
-        }
-
-        // Finds this layer:
-        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; });
-        if (this_layer_it == object->layers.end())
-            continue;
-        const Layer* this_layer = *this_layer_it;
-        unsigned int num_of_copies = object->_shifted_copies.size();
-
-        for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
-
-            for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
-                const auto& region = *object->print()->regions[region_id];
-
-                if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
-                    continue;
-
-
-                if (((!config.infill_first ? perimeters_done : !perimeters_done) || !object->config.wipe_into_objects) && region.config.wipe_into_infill) {
-                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
-                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
-                        if (volume_to_wipe <= 0.f)
-                                return 0.f;
-                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
-                            continue;
-
-                        // What extruder would this normally be printed with?
-                        unsigned int correct_extruder = get_extruder(fill, region);
-                        if (config.filament_soluble.get_at(correct_extruder)) // if this entity is meant to be soluble, keep it that way
-                            continue;
-
-                        if (!object->config.wipe_into_objects && !config.infill_first)  {
-                            // In this case we must check that the original extruder is used on this layer before the one we are overridding
-                            // (and the perimeters will be finished before the infill is printed):
-                            if (!config.infill_first && region.config.wipe_into_infill) {
-                                bool unused_yet = false;
-                                for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
-                                    if (layer_tools.extruders[i] == new_extruder)
-                                        unused_yet = true;
-                                    if (layer_tools.extruders[i] == correct_extruder)
-                                        break;
-                                }
-                                if (unused_yet)
-                                    continue;
-                            }
-                        }
-
-                        if (!layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
-                            layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies);
-                            volume_to_wipe -= fill->total_volume();
-                        }
-                    }
-                }
-
-
-                if (object->config.wipe_into_objects && (config.infill_first ? perimeters_done : !perimeters_done))
-                {
-                    ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
-                    for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
-                        if (volume_to_wipe <= 0.f)
-                            return 0.f;
-                        auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee);
-                        // What extruder would this normally be printed with?
-                        unsigned int correct_extruder = get_extruder(fill, region);
-                        if (config.filament_soluble.get_at(correct_extruder)) // if this entity is meant to be soluble, keep it that way
-                            continue;
-
-                        if (!layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
-                            layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies);
-                            volume_to_wipe -= fill->total_volume();
-                        }
-                    }
-                }
-            }
-        }
-    }
-    return std::max(0.f, volume_to_wipe);
-}
 
 
 std::string Print::output_filename()
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 8ae9b3689..f90fb500f 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -318,19 +318,15 @@ private:
     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
     PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
 
-    // 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(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
-
     // Has the calculation been canceled?
     tbb::atomic<bool>   m_canceled;
 };
 
 
 // Returns extruder this eec should be printed with, according to PrintRegion config
-static int get_extruder(const ExtrusionEntityCollection* fill, const PrintRegion &region) {
-    return is_infill(fill->role()) ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
-                                     std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region) {
+    return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+                                    std::max<int>(region.config.perimeter_extruder.value - 1, 0);
 }
 
 

From c11a163e08854164e1e1170b3ce4486644bebc84 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 27 Jun 2018 14:08:46 +0200
Subject: [PATCH 25/36] Correct extruder is used for dontcare extrusions

---
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 39 ++++++++++++++-----------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 34bb32e65..20f5318ea 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -179,15 +179,13 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
         }
     }
 
-    // Sort and remove duplicates, make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
-    for (auto lt_it=m_layer_tools.begin(); lt_it != m_layer_tools.end(); ++lt_it) {
-        sort_remove_duplicates(lt_it->extruders);
+    for (auto& layer : m_layer_tools) {
+        // Sort and remove duplicates
+        sort_remove_duplicates(layer.extruders);
 
-        if (lt_it->extruders.empty() && lt_it->has_object)
-            if (lt_it != m_layer_tools.begin())
-                lt_it->extruders.push_back(std::prev(lt_it)->extruders.back());
-            else
-                lt_it->extruders.push_back(1);
+        // make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
+        if (layer.extruders.empty() && layer.has_object)
+            layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
     }
 }
 
@@ -360,7 +358,8 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
 
 
 // This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
-void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies) {
+void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies)
+{
     something_overridden = true;
 
     auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
@@ -376,7 +375,8 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi
 
 
 // Finds last non-soluble extruder on the layer
-bool WipingExtrusions::is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const {
+bool WipingExtrusions::is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const
+{
     for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
         if (!print_config.filament_soluble.get_at(*extruders_it))
             return (*extruders_it == extruder);
@@ -385,12 +385,16 @@ bool WipingExtrusions::is_last_nonsoluble_on_layer(const PrintConfig& print_conf
 
 
 // Decides whether this entity could be overridden
-bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const {
-    if ((!is_infill(eec.role()) && !object.config.wipe_into_objects) ||
-        ((eec.role() == erTopSolidInfill || eec.role() == erGapFill) && !object.config.wipe_into_objects) ||
-        (is_infill(eec.role()) && !region.config.wipe_into_infill) ||
-        (print_config.filament_soluble.get_at(get_extruder(eec, region))) )
-            return false;
+bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
+{
+    if (print_config.filament_soluble.get_at(get_extruder(eec, region)))
+        return false;
+
+    if (object.config.wipe_into_objects)
+        return true;
+
+    if (!region.config.wipe_into_infill || eec.role() != erInternalInfill)
+        return false;
 
     return true;
 }
@@ -527,7 +531,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
 // so -1 was used as "print as usual".
 // The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
 // its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
-const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies) {
+const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies)
+{
     auto entity_map_it = entity_map.find(entity);
     if (entity_map_it == entity_map.end())
         entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;

From 54bd0af905b6592f813be46525422beaab946f53 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 27 Jun 2018 15:07:37 +0200
Subject: [PATCH 26/36] Infill wiping turned off by default and in some
 automatic tests

---
 t/combineinfill.t                       |  1 +
 t/fill.t                                |  1 +
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 13 +++++--------
 xs/src/libslic3r/PrintConfig.cpp        |  2 +-
 4 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/t/combineinfill.t b/t/combineinfill.t
index 5402a84f5..563ecb9c1 100644
--- a/t/combineinfill.t
+++ b/t/combineinfill.t
@@ -61,6 +61,7 @@ plan tests => 8;
     $config->set('infill_every_layers', 2);
     $config->set('perimeter_extruder', 1);
     $config->set('infill_extruder', 2);
+    $config->set('wipe_into_infill', 0);
     $config->set('support_material_extruder', 3);
     $config->set('support_material_interface_extruder', 3);
     $config->set('top_solid_layers', 0);
diff --git a/t/fill.t b/t/fill.t
index dd9eee487..5cbd568dd 100644
--- a/t/fill.t
+++ b/t/fill.t
@@ -201,6 +201,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
     $config->set('bottom_solid_layers', 0);
     $config->set('infill_extruder', 2);
     $config->set('infill_extrusion_width', 0.5);
+    $config->set('wipe_into_infill', 0);
     $config->set('fill_density', 40);
     $config->set('cooling', [ 0 ]);             # for preventing speeds from being altered
     $config->set('first_layer_speed', '100%');  # for preventing speeds from being altered
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 20f5318ea..761e83fcc 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -453,7 +453,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                     for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
 
-                        if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
+                        if (!is_overriddable(*fill, print.config, *object, region))
                             continue;
 
                         // What extruder would this normally be printed with?
@@ -467,9 +467,6 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                         if (!force_override && volume_to_wipe<=0)
                             continue;
 
-                        if (!is_overriddable(*fill, print.config, *object, region))
-                            continue;
-
                         if (!object->config.wipe_into_objects && !print.config.infill_first && !force_override) {
                             // In this case we must check that the original extruder is used on this layer before the one we are overridding
                             // (and the perimeters will be finished before the infill is printed):
@@ -493,12 +490,15 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                     }
                 }
 
-
+                // Now the same for perimeters - see comments above for explanation:
                 if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
                 {
                     const ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
                     for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+                        if (!is_overriddable(*fill, print.config, *object, region))
+                            continue;
+
                         // What extruder would this normally be printed with?
                         unsigned int correct_extruder = get_extruder(*fill, region);
                         bool force_override = false;
@@ -507,9 +507,6 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                         if (!force_override && volume_to_wipe<=0)
                             continue;
 
-                        if (!is_overriddable(*fill, print.config, *object, region))
-                            continue;
-
                         if (force_override || (!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
                             set_extruder_override(fill, copy, new_extruder, num_of_copies);
                             volume_to_wipe -= fill->total_volume();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 0833e13c8..c28c1404e 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1892,7 +1892,7 @@ PrintConfigDef::PrintConfigDef()
                      "This lowers the amount of waste but may result in longer print time "
                      " due to additional travel moves.");
     def->cli = "wipe-into-infill!";
-    def->default_value = new ConfigOptionBool(true);
+    def->default_value = new ConfigOptionBool(false);
 
     def = this->add("wipe_into_objects", coBool);
     def->category = L("Extruders");

From bb288f2a1b99a18d8776809a0154cf9e1026cc3a Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 27 Jun 2018 15:49:02 +0200
Subject: [PATCH 27/36] Fixed a crash when complete_objects was turned on

---
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 27 ++++++++++++++++---------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 761e83fcc..598d3bcc6 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -138,15 +138,19 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
             const PrintRegion &region = *object.print()->regions[region_id];
 
             if (! layerm->perimeters.entities.empty()) {
-                bool something_nonoverriddable = false;
-                for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
-                    if (!layer_tools.wiping_extrusions.is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
-                        something_nonoverriddable = true;
-                        break;
-                    }
+                bool something_nonoverriddable = true;
+
+                if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
+                    something_nonoverriddable = false;
+                    for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
+                        if (!layer_tools.wiping_extrusions.is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
+                            something_nonoverriddable = true;
+                            break;
+                        }
+                }
 
                 if (something_nonoverriddable)
-                    layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
+                        layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
 
                 layer_tools.has_object = true;
             }
@@ -164,10 +168,13 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
                 else if (role != erNone)
                     has_infill = true;
 
-                if (!something_nonoverriddable && !layer_tools.wiping_extrusions.is_overriddable(*fill, *m_print_config_ptr, object, region))
-                    something_nonoverriddable = true;
+                if (m_print_config_ptr) {
+                    if (!something_nonoverriddable && !layer_tools.wiping_extrusions.is_overriddable(*fill, *m_print_config_ptr, object, region))
+                        something_nonoverriddable = true;
+                }
             }
-            if (something_nonoverriddable)
+
+            if (something_nonoverriddable || !m_print_config_ptr)
             {
                 if (has_solid_infill)
                     layer_tools.extruders.push_back(region.config.solid_infill_extruder);

From 5bf795ec6f2b746f1934f99eaa522a05c98a4fa9 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 29 Jun 2018 12:26:22 +0200
Subject: [PATCH 28/36] Overriddable infills that were not overridden are now
 printed according to infill_first

---
 xs/src/libslic3r/GCode.cpp              |   2 +-
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 112 ++++++++++++++++++------
 xs/src/libslic3r/GCode/ToolOrdering.hpp |   5 +-
 xs/src/libslic3r/Print.cpp              |   1 +
 4 files changed, 90 insertions(+), 30 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 1271ee9ee..188993aeb 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1334,7 +1334,7 @@ void GCode::process_layer(
         if (objects_by_extruder_it == by_extruder.end())
             continue;
 
-        // We are almost ready to print. However, we must go through all the object twice and only print the overridden extrusions first (infill/primeter wiping feature):
+        // We are almost ready to print. However, we must go through all the object twice and only print the overridden extrusions first (infill/perimeter wiping feature):
         for (int print_wipe_extrusions=layer_tools.wiping_extrusions.is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
             for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
                 const size_t       layer_id     = &object_by_extruder - objects_by_extruder_it->second.data();
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 598d3bcc6..1987a0dae 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -381,13 +381,24 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi
 }
 
 
+// Finds first non-soluble extruder on the layer
+int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const
+{
+    for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
+        if (!print_config.filament_soluble.get_at(*extruders_it))
+            return (*extruders_it);
+
+    return (-1);
+}
+
 // Finds last non-soluble extruder on the layer
-bool WipingExtrusions::is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const
+int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const
 {
     for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
         if (!print_config.filament_soluble.get_at(*extruders_it))
-            return (*extruders_it == extruder);
-    return false;
+            return (*extruders_it);
+
+    return (-1);
 }
 
 
@@ -416,7 +427,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
     if (print.config.filament_soluble.get_at(new_extruder))
         return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
 
-    bool last_nonsoluble = is_last_nonsoluble_on_layer(print.config, layer_tools, new_extruder);
+    bool is_last_nonsoluble = ((int)new_extruder == last_nonsoluble_extruder_on_layer(print.config, layer_tools));
 
     // we will sort objects so that dedicated for wiping are at the beginning:
     PrintObjectPtrs object_list = print.objects;
@@ -430,15 +441,15 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
     // this is controlled by the following variable:
     bool perimeters_done = false;
 
-    for (int i=0 ; i<(int)object_list.size() ; ++i) {
-        const auto& object = object_list[i];
-
-        if (!perimeters_done && (i+1==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
+    for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
+        if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
             perimeters_done = true;
             i=-1;   // let's go from the start again
             continue;
         }
 
+        const auto& object = object_list[i];
+
         // Finds this layer:
         auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; });
         if (this_layer_it == object->layers.end())
@@ -455,9 +466,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                     continue;
 
 
-                if (((!print.config.infill_first ? perimeters_done : !perimeters_done) || !object->config.wipe_into_objects) && region.config.wipe_into_infill) {
-                    const ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills;
-                    for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
+                if ((!print.config.infill_first ? perimeters_done : !perimeters_done) || (!object->config.wipe_into_objects && region.config.wipe_into_infill)) {
+                    for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) {                      // iterate through all infill Collections
                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
 
                         if (!is_overriddable(*fill, print.config, *object, region))
@@ -466,15 +476,10 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                         // What extruder would this normally be printed with?
                         unsigned int correct_extruder = get_extruder(*fill, region);
 
-                        bool force_override = false;
-                        // If the extruder is not in layer tools - we MUST override it. This happens whenever all extrusions, that would normally
-                        // be printed with this extruder on this layer are "dont care" (part of infill/perimeter wiping):
-                        if (last_nonsoluble && std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder) == layer_tools.extruders.end())
-                            force_override = true;
-                        if (!force_override && volume_to_wipe<=0)
+                        if (volume_to_wipe<=0)
                             continue;
 
-                        if (!object->config.wipe_into_objects && !print.config.infill_first && !force_override) {
+                        if (!object->config.wipe_into_objects && !print.config.infill_first) {
                             // In this case we must check that the original extruder is used on this layer before the one we are overridding
                             // (and the perimeters will be finished before the infill is printed):
                             if ((!print.config.infill_first && region.config.wipe_into_infill)) {
@@ -490,7 +495,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                             }
                         }
 
-                        if (force_override || (!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {     // this infill will be used to wipe this extruder
+                        if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {     // this infill will be used to wipe this extruder
                             set_extruder_override(fill, copy, new_extruder, num_of_copies);
                             volume_to_wipe -= fill->total_volume();
                         }
@@ -500,21 +505,15 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                 // Now the same for perimeters - see comments above for explanation:
                 if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
                 {
-                    const ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters;
-                    for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
+                    for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {
                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
                         if (!is_overriddable(*fill, print.config, *object, region))
                             continue;
 
-                        // What extruder would this normally be printed with?
-                        unsigned int correct_extruder = get_extruder(*fill, region);
-                        bool force_override = false;
-                        if (last_nonsoluble && std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder) == layer_tools.extruders.end())
-                            force_override = true;
-                        if (!force_override && volume_to_wipe<=0)
+                        if (volume_to_wipe<=0)
                             continue;
 
-                        if (force_override || (!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
+                        if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
                             set_extruder_override(fill, copy, new_extruder, num_of_copies);
                             volume_to_wipe -= fill->total_volume();
                         }
@@ -528,6 +527,63 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
 
 
 
+// Called after all toolchanges on a layer were mark_infill_overridden. There might still be overridable entities,
+// that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
+// they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
+// them again and make sure we override it.
+void WipingExtrusions::ensure_perimeters_infills_order(const Print& print, const LayerTools& layer_tools)
+{
+    unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config, layer_tools);
+    unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config, layer_tools);
+
+    for (const PrintObject* object : print.objects) {
+        // Finds this layer:
+        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; });
+        if (this_layer_it == object->layers.end())
+            continue;
+        const Layer* this_layer = *this_layer_it;
+        unsigned int num_of_copies = object->_shifted_copies.size();
+
+        for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+            for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
+                const auto& region = *object->print()->regions[region_id];
+
+                if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+                    continue;
+
+                for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) {                      // iterate through all infill Collections
+                    auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+                    if (!is_overriddable(*fill, print.config, *object, region)
+                     || is_entity_overridden(fill, copy) )
+                        continue;
+
+                    // This infill could have been overridden but was not - unless we do somthing, it could be
+                    // printed before its perimeter, or not be printed at all (in case its original extruder has
+                    // not been added to LayerTools
+                    // Either way, we will now force-override it with something suitable:
+                    set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
+                }
+
+                // Now the same for perimeters - see comments above for explanation:
+                for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {                      // iterate through all perimeter Collections
+                    auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+                    if (!is_overriddable(*fill, print.config, *object, region)
+                     || is_entity_overridden(fill, copy) )
+                        continue;
+
+                    set_extruder_override(fill, copy, (print.config.infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
+                }
+            }
+        }
+    }
+}
+
+
+
+
+
+
 
 // Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
 // It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index 862b58f67..ac6bb480c 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -30,10 +30,13 @@ public:
     // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
     float mark_wiping_extrusions(const Print& print, const LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
 
+    void ensure_perimeters_infills_order(const Print& print, const LayerTools& layer_tools);
+
     bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
 
 private:
-    bool is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const;
+    int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const;
+    int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const;
 
     // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
     void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index fcbe74b85..fbbded7cb 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1143,6 +1143,7 @@ void Print::_make_wipe_tower()
                     current_extruder_id = extruder_id;
                 }
             }
+            layer_tools.wiping_extrusions.ensure_perimeters_infills_order(*this, layer_tools);
             if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
                 break;
         }

From bb80774e74b480ad1939c45b81be456ab9ba6083 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 9 Jul 2018 13:44:41 +0200
Subject: [PATCH 29/36] Infill purging - added fifth extruder into default
 setttings, cosmetic changes

---
 xs/src/libslic3r/GCode.cpp              | 5 ++++-
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 2 --
 xs/src/libslic3r/PrintConfig.cpp        | 8 ++++----
 xs/src/slic3r/GUI/Tab.cpp               | 2 --
 4 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 188993aeb..10404c90c 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1334,8 +1334,11 @@ void GCode::process_layer(
         if (objects_by_extruder_it == by_extruder.end())
             continue;
 
-        // We are almost ready to print. However, we must go through all the object twice and only print the overridden extrusions first (infill/perimeter wiping feature):
+        // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
         for (int print_wipe_extrusions=layer_tools.wiping_extrusions.is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
+            if (print_wipe_extrusions == 0)
+                gcode+="; PURGING FINISHED\n";
+
             for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
                 const size_t       layer_id     = &object_by_extruder - objects_by_extruder_it->second.data();
                 const PrintObject *print_object = layers[layer_id].object();
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 1987a0dae..219c1adfd 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -427,8 +427,6 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
     if (print.config.filament_soluble.get_at(new_extruder))
         return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
 
-    bool is_last_nonsoluble = ((int)new_extruder == last_nonsoluble_extruder_on_layer(print.config, layer_tools));
-
     // we will sort objects so that dedicated for wiping are at the beginning:
     PrintObjectPtrs object_list = print.objects;
     std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index c28c1404e..3a932822f 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -344,6 +344,7 @@ PrintConfigDef::PrintConfigDef()
     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 = this->add("extruder_clearance_height", coFloat);
     def->label = L("Height");
@@ -1887,7 +1888,7 @@ PrintConfigDef::PrintConfigDef()
 
     def = this->add("wipe_into_infill", coBool);
     def->category = L("Extruders");
-    def->label = L("Wiping into infill");
+    def->label = L("Purging into infill");
     def->tooltip = L("Wiping after toolchange will be preferentially done inside infills. "
                      "This lowers the amount of waste but may result in longer print time "
                      " due to additional travel moves.");
@@ -1896,11 +1897,10 @@ PrintConfigDef::PrintConfigDef()
 
     def = this->add("wipe_into_objects", coBool);
     def->category = L("Extruders");
-    def->label = L("Wiping into objects");
+    def->label = L("Purging into objects");
     def->tooltip = L("Objects will be used to wipe the nozzle after a toolchange to save material "
                      "that would otherwise end up in the wipe tower and decrease print time. "
-                     "Colours of the objects will be mixed as a result. (This setting is usually "
-                     "used on per-object basis.)");
+                     "Colours of the objects will be mixed as a result.");
     def->cli = "wipe-into-objects!";
     def->default_value = new ConfigOptionBool(false);
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 010934570..4b1d28f72 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -947,8 +947,6 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_width");
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
         optgroup->append_single_option_line("wipe_tower_bridging");
-        optgroup->append_single_option_line("wipe_into_infill");
-        optgroup->append_single_option_line("wipe_into_objects");
 
 		optgroup = page->new_optgroup(_(L("Advanced")));
 		optgroup->append_single_option_line("interface_shells");

From 4c823b840fd59a94b8ffe3183e201305844af9e1 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 9 Jul 2018 14:43:32 +0200
Subject: [PATCH 30/36] Fix of previous commit

---
 xs/src/slic3r/GUI/Preset.cpp | 2 +-
 xs/src/slic3r/GUI/Tab.cpp    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 84f685533..0ccf4d481 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -298,7 +298,7 @@ const std::vector<std::string>& Preset::print_options()
         "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", 
         "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", 
         "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
-        "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "wipe_into_infill", "wipe_into_objects", "compatible_printers",
+        "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers",
         "compatible_printers_condition","inherits"
     };
     return s_opts;
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 4b1d28f72..dbf892110 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -1235,7 +1235,7 @@ void TabPrint::update()
 	get_field("standby_temperature_delta")->toggle(have_ooze_prevention);
 
 	bool have_wipe_tower = m_config->opt_bool("wipe_tower");
-	for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_into_infill", "wipe_tower_bridging"})
+	for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"})
 		get_field(el)->toggle(have_wipe_tower);
 
 	m_recommended_thin_wall_thickness_description_line->SetText(

From e44480d61f53d8b846f647cdacf2ef1c48735747 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 10 Jul 2018 13:02:43 +0200
Subject: [PATCH 31/36] Supports were printed twice if synchronized with object
 layers, added always-on settings in ObjectSettingDialog

---
 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 17 +++++++++++++++--
 xs/src/libslic3r/GCode.cpp                |  2 +-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 1ec0ce1cb..a20a241f7 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -322,7 +322,13 @@ sub selection_changed {
         }
         # get default values
         my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
-        
+
+       # decide which settings will be shown by default
+        if ($itemData->{type} eq 'object') {
+            $config->set_ifndef('wipe_into_objects', 0);
+            $config->set_ifndef('wipe_into_infill', 0);
+        }
+
         # append default extruder
         push @opt_keys, 'extruder';
         $default_config->set('extruder', 0);
@@ -330,7 +336,14 @@ sub selection_changed {
         $self->{settings_panel}->set_default_config($default_config);
         $self->{settings_panel}->set_config($config);
         $self->{settings_panel}->set_opt_keys(\@opt_keys);
-        $self->{settings_panel}->set_fixed_options([qw(extruder)]);
+
+        # disable minus icon to remove the settings
+        if ($itemData->{type} eq 'object') {
+            $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
+	} else {
+            $self->{settings_panel}->set_fixed_options([qw(extruder)]);
+        }
+
         $self->{settings_panel}->enable;
     }
     
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index 10404c90c..f3813a56a 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1365,7 +1365,7 @@ void GCode::process_layer(
                         m_avoid_crossing_perimeters.use_external_mp_once = true;
                     m_last_obj_copy = this_object_copy;
                     this->set_origin(unscale(copy.x), unscale(copy.y));
-                    if (object_by_extruder.support != nullptr) {
+                    if (object_by_extruder.support != nullptr && !print_wipe_extrusions) {
                         m_layer = layers[layer_id].support_layer;
                         gcode += this->extrude_support(
                             // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.

From 2454c566ff2e865f8034d7dc50256a87128084d0 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 10 Jul 2018 15:39:47 +0200
Subject: [PATCH 32/36] Changing number of copies invalidates the wipe tower
 (and thus forces recalculation of the purging extrusions)

---
 xs/src/libslic3r/PrintObject.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index bd01ec3aa..7ac165864 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -93,6 +93,7 @@ bool PrintObject::set_copies(const Points &points)
     
     bool invalidated = this->_print->invalidate_step(psSkirt);
     invalidated |= this->_print->invalidate_step(psBrim);
+    invalidated |= this->_print->invalidate_step(psWipeTower);
     return invalidated;
 }
 

From 1a2223a0a53d8cb96641fa142b3bf1f382a0d82d Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 11 Jul 2018 14:46:13 +0200
Subject: [PATCH 33/36] WipingExtrusions functions now don't need a reference
 to LayerTools

---
 xs/src/libslic3r/GCode.cpp              |  8 ++++----
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 24 ++++++++++++++----------
 xs/src/libslic3r/GCode/ToolOrdering.hpp | 19 ++++++++++++++-----
 xs/src/libslic3r/Print.cpp              | 13 +++++++++++--
 xs/src/libslic3r/Print.hpp              | 11 +++--------
 5 files changed, 46 insertions(+), 29 deletions(-)

diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index f3813a56a..72294b7a3 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -1239,11 +1239,11 @@ void GCode::process_layer(
                             continue;
 
                         // This extrusion is part of certain Region, which tells us which extruder should be used for it:
-                        int correct_extruder_id = get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+                        int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
                                                                            std::max<int>(region.config.perimeter_extruder.value - 1, 0);
 
                         // Let's recover vector of extruder overrides:
-                        const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions.get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
+                        const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
 
                         // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
                         for (unsigned int extruder : layer_tools.extruders)
@@ -1335,7 +1335,7 @@ void GCode::process_layer(
             continue;
 
         // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
-        for (int print_wipe_extrusions=layer_tools.wiping_extrusions.is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
+        for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
             if (print_wipe_extrusions == 0)
                 gcode+="; PURGING FINISHED\n";
 
@@ -1373,7 +1373,7 @@ void GCode::process_layer(
                         m_layer = layers[layer_id].layer();
                     }
                     for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
-                        const auto& by_region_specific = layer_tools.wiping_extrusions.is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
+                        const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
 
                         if (print.config.infill_first) {
                             gcode += this->extrude_infill(print, by_region_specific);
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 219c1adfd..0fe7b0c4e 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -143,7 +143,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
                 if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
                     something_nonoverriddable = false;
                     for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
-                        if (!layer_tools.wiping_extrusions.is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
+                        if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
                             something_nonoverriddable = true;
                             break;
                         }
@@ -169,7 +169,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
                     has_infill = true;
 
                 if (m_print_config_ptr) {
-                    if (!something_nonoverriddable && !layer_tools.wiping_extrusions.is_overriddable(*fill, *m_print_config_ptr, object, region))
+                    if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region))
                         something_nonoverriddable = true;
                 }
             }
@@ -382,8 +382,9 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi
 
 
 // Finds first non-soluble extruder on the layer
-int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const
+int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
 {
+    const LayerTools& lt = *m_layer_tools;
     for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
         if (!print_config.filament_soluble.get_at(*extruders_it))
             return (*extruders_it);
@@ -392,8 +393,9 @@ int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& prin
 }
 
 // Finds last non-soluble extruder on the layer
-int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const
+int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
 {
+    const LayerTools& lt = *m_layer_tools;
     for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
         if (!print_config.filament_soluble.get_at(*extruders_it))
             return (*extruders_it);
@@ -405,7 +407,7 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print
 // Decides whether this entity could be overridden
 bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
 {
-    if (print_config.filament_soluble.get_at(get_extruder(eec, region)))
+    if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
         return false;
 
     if (object.config.wipe_into_objects)
@@ -420,8 +422,9 @@ 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, const LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe)
+float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe)
 {
+    const LayerTools& layer_tools = *m_layer_tools;
     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 
     if (print.config.filament_soluble.get_at(new_extruder))
@@ -472,7 +475,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
                             continue;
 
                         // What extruder would this normally be printed with?
-                        unsigned int correct_extruder = get_extruder(*fill, region);
+                        unsigned int correct_extruder = Print::get_extruder(*fill, region);
 
                         if (volume_to_wipe<=0)
                             continue;
@@ -529,10 +532,11 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTo
 // that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
 // they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
 // them again and make sure we override it.
-void WipingExtrusions::ensure_perimeters_infills_order(const Print& print, const LayerTools& layer_tools)
+void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
 {
-    unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config, layer_tools);
-    unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config, layer_tools);
+    const LayerTools& layer_tools = *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);
 
     for (const PrintObject* object : print.objects) {
         // Finds this layer:
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index ac6bb480c..34304d712 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -28,15 +28,17 @@ 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, const LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe);
+    float mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe);
 
-    void ensure_perimeters_infills_order(const Print& print, const LayerTools& layer_tools);
+    void ensure_perimeters_infills_order(const Print& print);
 
     bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
 
+    void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
+
 private:
-    int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const;
-    int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config, const LayerTools& lt) const;
+    int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
+    int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
 
     // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
     void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
@@ -48,6 +50,7 @@ private:
 
     std::map<const ExtrusionEntity*, std::vector<int>> entity_map;  // to keep track of who prints what
     bool something_overridden = false;
+    const LayerTools* m_layer_tools;    // so we know which LayerTools object this belongs to
 };
 
 
@@ -80,8 +83,14 @@ public:
     size_t                      wipe_tower_partitions;
     coordf_t 					wipe_tower_layer_height;
 
+    WipingExtrusions& wiping_extrusions() {
+        m_wiping_extrusions.set_layer_tools_ptr(this);
+        return m_wiping_extrusions;
+    }
+
+private:
     // This object holds list of extrusion that will be used for extruder wiping
-    WipingExtrusions wiping_extrusions;
+    WipingExtrusions m_wiping_extrusions;
 };
 
 
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index fbbded7cb..f8caa4786 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1137,13 +1137,13 @@ void Print::_make_wipe_tower()
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];    // total volume to wipe after this toolchange
 
                     if (!first_layer)   // unless we're on the first layer, try to assign some infills/objects for the wiping:
-                        volume_to_wipe = layer_tools.wiping_extrusions.mark_wiping_extrusions(*this, layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
+                        volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
 
                     wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
                     current_extruder_id = extruder_id;
                 }
             }
-            layer_tools.wiping_extrusions.ensure_perimeters_infills_order(*this, layer_tools);
+            layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
             if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
                 break;
         }
@@ -1215,4 +1215,13 @@ void Print::set_status(int percent, const std::string &message)
     printf("Print::status %d => %s\n", percent, message.c_str());
 }
 
+
+// Returns extruder this eec should be printed with, according to PrintRegion config
+int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region)
+{
+    return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+                                    std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+}
+
+
 }
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index f90fb500f..3ea7ffb68 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -286,6 +286,9 @@ public:
     bool has_support_material() const;
     void auto_assign_extruders(ModelObject* model_object) const;
 
+    // Returns extruder this eec should be printed with, according to PrintRegion config:
+    static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
+
     void _make_skirt();
     void _make_brim();
 
@@ -323,14 +326,6 @@ private:
 };
 
 
-// Returns extruder this eec should be printed with, according to PrintRegion config
-static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region) {
-    return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
-                                    std::max<int>(region.config.perimeter_extruder.value - 1, 0);
-}
-
-
-
 #define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
 #define FOREACH_REGION(print, region)       FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
 #define FOREACH_OBJECT(print, object)       FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)

From 103c7eda8a7c9cceb39696f010dbff2d2800d622 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 13 Jul 2018 11:25:22 +0200
Subject: [PATCH 34/36] Trying to make sure infill_first (or otherwise) is
 respected

---
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 54 ++++++++++++++++---------
 xs/src/libslic3r/GCode/ToolOrdering.hpp |  2 +
 2 files changed, 37 insertions(+), 19 deletions(-)

diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 0fe7b0c4e..46ba0731b 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -15,6 +15,24 @@
 
 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
+{
+    if (a==b)
+        return false;
+
+    for (auto extruder : extruders) {
+        if (extruder == a)
+            return true;
+        if (extruder == b)
+            return false;
+    }
+
+    return false;
+}
+
+
 // 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)
@@ -424,7 +442,7 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
 // and returns volume that is left to be wiped on the wipe tower.
 float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe)
 {
-    const LayerTools& layer_tools = *m_layer_tools;
+    const LayerTools& lt = *m_layer_tools;
     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 
     if (print.config.filament_soluble.get_at(new_extruder))
@@ -452,7 +470,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
         const auto& object = object_list[i];
 
         // Finds this layer:
-        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; });
+        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
         if (this_layer_it == object->layers.end())
             continue;
         const Layer* this_layer = *this_layer_it;
@@ -480,21 +498,11 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
                         if (volume_to_wipe<=0)
                             continue;
 
-                        if (!object->config.wipe_into_objects && !print.config.infill_first) {
+                        if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill)
                             // In this case we must check that the original extruder is used on this layer before the one we are overridding
                             // (and the perimeters will be finished before the infill is printed):
-                            if ((!print.config.infill_first && region.config.wipe_into_infill)) {
-                                bool unused_yet = false;
-                                for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) {
-                                    if (layer_tools.extruders[i] == new_extruder)
-                                        unused_yet = true;
-                                    if (layer_tools.extruders[i] == correct_extruder)
-                                        break;
-                                }
-                                if (unused_yet)
-                                    continue;
-                            }
-                        }
+                            if (!lt.is_extruder_order(region.config.perimeter_extruder - 1, new_extruder))
+                                continue;
 
                         if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {     // this infill will be used to wipe this extruder
                             set_extruder_override(fill, copy, new_extruder, num_of_copies);
@@ -534,13 +542,13 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
 // them again and make sure we override it.
 void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
 {
-    const LayerTools& layer_tools = *m_layer_tools;
+    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);
 
     for (const PrintObject* object : print.objects) {
         // Finds this layer:
-        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; });
+        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
         if (this_layer_it == object->layers.end())
             continue;
         const Layer* this_layer = *this_layer_it;
@@ -560,11 +568,19 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
                      || is_entity_overridden(fill, copy) )
                         continue;
 
-                    // This infill could have been overridden but was not - unless we do somthing, it could be
+                    // This infill could have been overridden but was not - unless we do something, it could be
                     // printed before its perimeter, or not be printed at all (in case its original extruder has
                     // not been added to LayerTools
                     // Either way, we will now force-override it with something suitable:
-                    set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
+                    if (print.config.infill_first
+                    || lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder    // !infill_first, but perimeter is already printed when last extruder prints
+                    || std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
+                      )
+                        set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
+                    else {
+                        // In this case we can (and should) leave it to be printed normally.
+                        // Force overriding would mean it gets printed before its perimeter.
+                    }
                 }
 
                 // Now the same for perimeters - see comments above for explanation:
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index 34304d712..13e0212f1 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -69,6 +69,8 @@ public:
     bool operator< (const LayerTools &rhs) const { return print_z - EPSILON <  rhs.print_z; }
     bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
 
+    bool is_extruder_order(unsigned int a, unsigned int b) const;
+
     coordf_t 					print_z;
     bool 						has_object;
     bool						has_support;

From 9966f8d88d300322be6cd55671803ed23646f373 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 13 Jul 2018 11:53:50 +0200
Subject: [PATCH 35/36] Respect perimeter-infill order for purging only objects

---
 xs/src/libslic3r/GCode/ToolOrdering.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 46ba0731b..f1dbbfc1e 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -573,6 +573,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
                     // not been added to LayerTools
                     // Either way, we will now force-override it with something suitable:
                     if (print.config.infill_first
+                    || object->config.wipe_into_objects  // in this case the perimeter is overridden, so we can override by the last one safely
                     || lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder    // !infill_first, but perimeter is already printed when last extruder prints
                     || std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
                       )

From 3dfd6e64d90c74e8ee3b63087b0503019f819280 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 13 Jul 2018 13:16:38 +0200
Subject: [PATCH 36/36] Enabled inflill/object wiping for the first layer

---
 xs/src/libslic3r/Print.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index f8caa4786..79bca7e89 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1136,8 +1136,8 @@ void Print::_make_wipe_tower()
                 if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];    // total volume to wipe after this toolchange
 
-                    if (!first_layer)   // unless we're on the first layer, try to assign some infills/objects for the wiping:
-                        volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
+                    // try to assign some infills/objects for the wiping:
+                    volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
 
                     wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
                     current_extruder_id = extruder_id;