From 0e9e487930b3172bcf21580fedb2adc1232eac56 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 28 Nov 2017 17:32:11 +0100
Subject: [PATCH 001/118] First implementation of wipe tower rotation

---
 xs/src/libslic3r/GCode/WipeTower.hpp        | 10 ++++
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 52 +++++++++++++++------
 2 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index c5fa27392..329d92b22 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -23,6 +23,16 @@ public:
 		xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
 		bool operator==(const xy &rhs) { return x == rhs.x && y == rhs.y; }
 		bool operator!=(const xy &rhs) { return x != rhs.x || y != rhs.y; }
+		
+		// Rotate the point around given point about given angle (in degrees)
+		xy rotate(const xy& origin, float angle) const {
+			xy out(0,0);
+			angle *= M_PI/180.;
+			out.x=(x-origin.x) * cos(angle)  -  (y-origin.y) * sin(angle);
+			out.y=(x-origin.x) * sin(angle)  +  (y-origin.y) * cos(angle);
+			return out+origin;
+		}
+			
 		float x;
 		float y;
 	};
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 951d71075..5a7ee0002 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -14,6 +14,9 @@
 #define strcasecmp _stricmp
 #endif
 
+#define ROTATION_ANGLE 30
+
+
 namespace Slic3r
 {
 
@@ -47,6 +50,9 @@ public:
 
 	Writer& 			 set_extrusion_flow(float flow)
 		{ m_extrusion_flow = flow; return *this; }
+		
+	Writer&				 set_rotation(WipeTower::xy pos,float angle)
+		{ m_wipe_tower_pos = pos; m_angle_deg = angle; return (*this); }
 
 	// 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
@@ -67,6 +73,7 @@ public:
 	float                y()     const { return m_current_pos.y; }
 	const WipeTower::xy& start_pos() const { return m_start_pos; }
 	const WipeTower::xy& pos()   const { return m_current_pos; }
+	const WipeTower::xy  printer_pos() const { return m_current_pos.rotate(m_wipe_tower_pos,m_angle_deg); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
 
 	// Extrude with an explicitely provided amount of extrusion.
@@ -79,29 +86,43 @@ public:
 		float dx = x - m_current_pos.x;
 		float dy = y - m_current_pos.y;
 		double len = sqrt(dx*dx+dy*dy);
+		
+		// For rotated wipe tower, transform position to printer coordinates
+		//Q zavadet nove promenne? rotace o zaporny uhel?
+		WipeTower::xy rotated_current_pos( m_current_pos.rotate(m_wipe_tower_pos,m_angle_deg) );
+		WipeTower::xy rot( WipeTower::xy(x,y).rotate(m_wipe_tower_pos,m_angle_deg ) );
+						
+		
 		if (! m_preview_suppressed && e > 0.f && len > 0.) {
 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
 			// This is left zero if it is a travel move.
 			float width = float(double(e) * m_filament_area / (len * m_layer_height));
 			// Correct for the roundings of a squished extrusion.
 			width += float(m_layer_height * (1. - M_PI / 4.));
-			if (m_extrusions.empty() || m_extrusions.back().pos != m_current_pos)
-				m_extrusions.emplace_back(WipeTower::Extrusion(m_current_pos, 0, m_current_tool));
-			m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(x, y), width, m_current_tool));
+			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_gcode += "G1";
-		if (x != m_current_pos.x)
-			m_gcode += set_format_X(x);
-		if (y != m_current_pos.y)
-			m_gcode += set_format_Y(y);
+		if (rot.x != rotated_current_pos.x)
+			m_gcode += set_format_X(rot.x);
+		if (rot.y != rotated_current_pos.y)
+			m_gcode += set_format_Y(rot.y);
+			
+		// Transform current position back to wipe tower coordinates (was updated by set_format_X)
+		m_current_pos.x = x;
+		m_current_pos.y = y;
 
 		if (e != 0.f)
 			m_gcode += set_format_E(e);
 
 		if (f != 0.f && f != m_current_feedrate)
 			m_gcode += set_format_F(f);
-
+		
+		
+		
+		
 		// Update the elapsed time with a rough estimate.
 		m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
 		m_gcode += "\n";
@@ -278,6 +299,8 @@ private:
 	std::vector<WipeTower::Extrusion> m_extrusions;
 	float         m_elapsed_time;
 	const double  m_filament_area = 0.25*M_PI*1.75*1.75;
+	float   	  m_angle_deg = 0;
+	WipeTower::xy m_wipe_tower_pos;
 
 	std::string   set_format_X(float x) {
 		char buf[64];
@@ -338,7 +361,7 @@ public:
 };
 */
 
-} // namespace PrusaMultiMaterial
+}; // namespace PrusaMultiMaterial
 
 WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name)
 {
@@ -470,7 +493,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
 	result.start_pos  	= writer.start_pos();
-	result.end_pos 	  	= writer.pos();
+	result.end_pos 	  	= writer.printer_pos();
 	return result;
 }
 
@@ -525,6 +548,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		  .set_z(m_z_pos)
 		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
+		  .set_rotation(this->m_wipe_tower_pos,ROTATION_ANGLE)
 		  .append(";--------------------\n"
 			 	  "; CP TOOLCHANGE START\n")
 		  .comment_with_value(" toolchange #", m_num_tool_changes)
@@ -605,7 +629,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
 	result.start_pos  	= writer.start_pos();
-	result.end_pos 	  	= writer.pos();
+	result.end_pos 	  	= writer.printer_pos();
 	return result;
 }
 
@@ -622,6 +646,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 		  .set_z(m_z_pos)
 		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
+		  .set_rotation(this->m_wipe_tower_pos,ROTATION_ANGLE)
 		  .append(
 			";-------------------------------------\n"
 			"; CP WIPE TOWER FIRST LAYER BRIM START\n");
@@ -705,7 +730,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
 	result.start_pos  	= writer.start_pos();
-	result.end_pos 	  	= writer.pos();
+	result.end_pos 	  	= writer.printer_pos();
 	return result;
 }
 
@@ -910,6 +935,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 		  .set_z(m_z_pos)
 		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
+		  .set_rotation(this->m_wipe_tower_pos,ROTATION_ANGLE)
 		  .append(";--------------------\n"
 				  "; CP EMPTY GRID START\n")
 		  // m_num_layer_changes is incremented by set_z, so it is 1 based.
@@ -1025,7 +1051,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
 	result.start_pos 	= writer.start_pos();
-	result.end_pos 	  	= writer.pos();
+	result.end_pos 	  	= writer.printer_pos();
 	return result;
 }
 

From 2921302fe9602dae3974cb8bbdaea8dcb17677c4 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 30 Nov 2017 12:08:22 +0100
Subject: [PATCH 002/118] GUI integration of rotation angle setting

---
 lib/Slic3r/GUI/Plater.pm                    | 2 +-
 lib/Slic3r/GUI/Tab.pm                       | 3 ++-
 xs/src/libslic3r/GCode/WipeTower.hpp        | 2 +-
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 7 +++----
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 8 +++++++-
 xs/src/libslic3r/Print.cpp                  | 3 ++-
 xs/src/libslic3r/PrintConfig.cpp            | 7 +++++++
 xs/src/libslic3r/PrintConfig.hpp            | 2 ++
 xs/src/slic3r/GUI/Preset.cpp                | 2 +-
 9 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index a6915bb7c..cdb03e791 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -52,7 +52,7 @@ sub new {
         bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
         serial_port serial_speed octoprint_host octoprint_apikey
         nozzle_diameter single_extruder_multi_material 
-        wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
+        wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe wipe_tower_rotation_angle extruder_colour filament_colour
     )]);
     # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
     $self->{model} = Slic3r::Model->new;
diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm
index 907be7872..d43c33ff1 100644
--- a/lib/Slic3r/GUI/Tab.pm
+++ b/lib/Slic3r/GUI/Tab.pm
@@ -725,6 +725,7 @@ sub build {
             $optgroup->append_single_option_line('wipe_tower_y');
             $optgroup->append_single_option_line('wipe_tower_width');
             $optgroup->append_single_option_line('wipe_tower_per_color_wipe');
+            $optgroup->append_single_option_line('wipe_tower_rotation_angle');
         }
         {
             my $optgroup = $page->new_optgroup('Advanced');
@@ -1032,7 +1033,7 @@ sub _update {
 
     my $have_wipe_tower = $config->wipe_tower;
     $self->get_field($_)->toggle($have_wipe_tower)
-        for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe);
+        for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe wipe_tower_rotation_angle);
 
     $self->Thaw;
 }
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index f55fd5c2f..8a97333de 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -32,7 +32,7 @@ public:
 			out.y=(x-origin.x) * sin(angle)  +  (y-origin.y) * cos(angle);
 			return out+origin;
 		}
-			
+		
 		float x;
 		float y;
 	};
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 5a7ee0002..3df1e9d4a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -14,7 +14,6 @@
 #define strcasecmp _stricmp
 #endif
 
-#define ROTATION_ANGLE 30
 
 
 namespace Slic3r
@@ -548,7 +547,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		  .set_z(m_z_pos)
 		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
-		  .set_rotation(this->m_wipe_tower_pos,ROTATION_ANGLE)
+		  .set_rotation(this->m_wipe_tower_pos,m_wipe_tower_rotation_angle)
 		  .append(";--------------------\n"
 			 	  "; CP TOOLCHANGE START\n")
 		  .comment_with_value(" toolchange #", m_num_tool_changes)
@@ -646,7 +645,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 		  .set_z(m_z_pos)
 		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
-		  .set_rotation(this->m_wipe_tower_pos,ROTATION_ANGLE)
+		  .set_rotation(this->m_wipe_tower_pos,m_wipe_tower_rotation_angle)
 		  .append(
 			";-------------------------------------\n"
 			"; CP WIPE TOWER FIRST LAYER BRIM START\n");
@@ -935,7 +934,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 		  .set_z(m_z_pos)
 		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
-		  .set_rotation(this->m_wipe_tower_pos,ROTATION_ANGLE)
+		  .set_rotation(this->m_wipe_tower_pos,m_wipe_tower_rotation_angle)
 		  .append(";--------------------\n"
 				  "; CP EMPTY GRID START\n")
 		  // m_num_layer_changes is incremented by set_z, so it is 1 based.
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 15a96d367..fd9c55cb7 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -8,6 +8,8 @@
 
 #include "WipeTower.hpp"
 
+
+
 namespace Slic3r
 {
 
@@ -39,10 +41,11 @@ public:
 	// y			-- y coordinates of wipe tower in mm ( left bottom corner )
 	// width		-- width of wipe tower in mm ( default 60 mm - leave as it is )
 	// wipe_area	-- space available for one toolchange in mm
-	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, unsigned int initial_tool) :
+	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, unsigned int initial_tool) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
 		m_wipe_area(wipe_area),
+		m_wipe_tower_rotation_angle(rotation_angle),
 		m_z_pos(0.f),
 		m_current_tool(initial_tool)
  	{
@@ -96,6 +99,7 @@ public:
 		// Extrusion rate for an extrusion aka perimeter width 0.35mm.
 		// Clamp the extrusion height to a 0.2mm layer height, independent of the nozzle diameter.
 //		m_extrusion_flow = std::min(0.2f, layer_height) * 0.145f;
+
 		// Use a strictly
 		m_extrusion_flow = layer_height * 0.145f;
 	}
@@ -149,6 +153,8 @@ private:
 	float  			m_wipe_tower_width;
 	// Per color Y span.
 	float  			m_wipe_area;
+	// Wipe tower rotation angle in degrees (with respect to x axis
+	float			m_wipe_tower_rotation_angle;
 	// Current Z position.
 	float  			m_z_pos 			= 0.f;
 	// Current layer height.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 78ebf9294..3bd07d3eb 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -191,6 +191,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "wipe_tower_y"
             || opt_key == "wipe_tower_width"
             || opt_key == "wipe_tower_per_color_wipe"
+            || opt_key == "wipe_tower_rotation_angle"
             || opt_key == "z_offset") {
             steps.emplace_back(psWipeTower);
         } else if (
@@ -983,7 +984,7 @@ void Print::_make_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_per_color_wipe.value),
-        initial_extruder_id);
+        float(this->config.wipe_tower_rotation_angle.value), initial_extruder_id);
     
     //wipe_tower.set_retract();
     //wipe_tower.set_zhop();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 217f9bdef..65ae56961 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1708,6 +1708,13 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "wipe-tower-per-color-wipe=f";
     def->default_value = new ConfigOptionFloat(15.);
 
+    def = this->add("wipe_tower_rotation_angle", coFloat);
+    def->label = "Wipe tower rotation angle";
+    def->tooltip = "Wipe tower rotation angle with respect to x-asis ";
+    def->sidetext = "degrees";
+    def->cli = "wipe-tower-rotation-angle=f";
+    def->default_value = new ConfigOptionFloat(0.);
+
     def = this->add("xy_size_compensation", coFloat);
     def->label = "XY Size Compensation";
     def->category = "Advanced";
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index ab58aa356..ea4e07dd9 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -607,6 +607,7 @@ public:
     ConfigOptionFloat               wipe_tower_y;
     ConfigOptionFloat               wipe_tower_width;
     ConfigOptionFloat               wipe_tower_per_color_wipe;
+    ConfigOptionFloat               wipe_tower_rotation_angle;
     ConfigOptionFloat               z_offset;
     
 protected:
@@ -671,6 +672,7 @@ protected:
         OPT_PTR(wipe_tower_y);
         OPT_PTR(wipe_tower_width);
         OPT_PTR(wipe_tower_per_color_wipe);
+        OPT_PTR(wipe_tower_rotation_angle);
         OPT_PTR(z_offset);
     }
 };
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index e4b0448cf..2d6ff59b0 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -178,7 +178,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_per_color_wipe",
+        "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle",
         "compatible_printers"
     };
     return s_opts;

From a733df8f375901ed3fbd0a1f0721ce5bb4c33407 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 30 Nov 2017 14:43:47 +0100
Subject: [PATCH 003/118] Wipe tower rotation - preview box

---
 lib/Slic3r/GUI/Plater/3D.pm   |  2 +-
 xs/src/slic3r/GUI/3DScene.cpp | 10 ++++++----
 xs/src/slic3r/GUI/3DScene.hpp |  4 ++--
 xs/xsp/GUI_3DScene.xsp        |  2 +-
 4 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index 503a3d159..2bc7dd90f 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -198,7 +198,7 @@ sub reload_scene {
             $self->volumes->load_wipe_tower_preview(1000, 
                 $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, 
                 $self->{config}->wipe_tower_width, $self->{config}->wipe_tower_per_color_wipe * ($extruders_count - 1),
-                $self->{model}->bounding_box->z_max, $self->UseVBOs);
+                $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs);
         }
     }
 }
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 81dbcdacc..6679b5515 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -301,17 +301,19 @@ std::vector<int> GLVolumeCollection::load_object(
 
 
 int GLVolumeCollection::load_wipe_tower_preview(
-    int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs)
+    int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
 {
     float color[4] = { 1.0f, 1.0f, 0.0f, 0.5f };
     this->volumes.emplace_back(new GLVolume(color));
     GLVolume &v = *this->volumes.back();
     auto mesh = make_cube(width, depth, height);
-    v.indexed_vertex_array.load_mesh_flat_shading(mesh);
-    v.origin = Pointf3(pos_x, pos_y, 0.);
+	v.origin = Pointf3(pos_x, pos_y, 0.);
+	Point origin = Point(pos_x,pos_y);
+	mesh.rotate(rotation_angle,&origin);
+	v.indexed_vertex_array.load_mesh_flat_shading(mesh);
     // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
     v.bounding_box = v.indexed_vertex_array.bounding_box();
-    v.indexed_vertex_array.finalize_geometry(use_VBOs);
+	v.indexed_vertex_array.finalize_geometry(use_VBOs);
     v.composite_id = obj_idx * 1000000;
     v.select_group_id = obj_idx * 1000000;
     v.drag_group_id = obj_idx * 1000;
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index 27eeb7ca8..3649db779 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -226,7 +226,7 @@ public:
         const std::string        &drag_by);
 
     int load_wipe_tower_preview(
-        int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs);
+        int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
 
     // Bounding box of this volume, in unscaled coordinates.
     BoundingBoxf3       bounding_box;
@@ -315,7 +315,7 @@ public:
         bool                     use_VBOs);
 
     int load_wipe_tower_preview(
-        int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs);
+        int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
 
     // Render the volumes by OpenGL.
     void render_VBOs() const;
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 13f0d42f2..d19744f25 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -76,7 +76,7 @@
 
     std::vector<int> load_object(ModelObject *object, int obj_idx, std::vector<int> instance_idxs, std::string color_by, std::string select_by, std::string drag_by, bool use_VBOs);
 
-    int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs);
+    int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
 
     void erase()
         %code{% THIS->clear(); %};

From 55570119f7f8fe9d218d2d895e44f487b764df0a Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 30 Nov 2017 16:11:20 +0100
Subject: [PATCH 004/118] Correction of previous commit

---
 xs/src/slic3r/GUI/3DScene.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 6679b5515..bb4183bdf 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -307,10 +307,9 @@ int GLVolumeCollection::load_wipe_tower_preview(
     this->volumes.emplace_back(new GLVolume(color));
     GLVolume &v = *this->volumes.back();
     auto mesh = make_cube(width, depth, height);
-	v.origin = Pointf3(pos_x, pos_y, 0.);
-	Point origin = Point(pos_x,pos_y);
-	mesh.rotate(rotation_angle,&origin);
+	mesh.rotate_z(rotation_angle*M_PI/180.);
 	v.indexed_vertex_array.load_mesh_flat_shading(mesh);
+	v.origin = Pointf3(pos_x, pos_y, 0.);
     // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
     v.bounding_box = v.indexed_vertex_array.bounding_box();
 	v.indexed_vertex_array.finalize_geometry(use_VBOs);

From cb84a6cfceb8151ddd0d8ec647ffd87cf4c9d060 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 1 Dec 2017 11:10:01 +0100
Subject: [PATCH 005/118] Wipe tower rotation - bug fix (now returning
 start_pos correctly)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 3df1e9d4a..cec3e0cf9 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -70,9 +70,9 @@ public:
 	const std::vector<WipeTower::Extrusion>& extrusions() const { return m_extrusions; }
 	float                x()     const { return m_current_pos.x; }
 	float                y()     const { return m_current_pos.y; }
-	const WipeTower::xy& start_pos() const { return m_start_pos; }
 	const WipeTower::xy& pos()   const { return m_current_pos; }
-	const WipeTower::xy  printer_pos() const { return m_current_pos.rotate(m_wipe_tower_pos,m_angle_deg); }
+	const WipeTower::xy  start_pos_rotated() const { return m_start_pos.rotate(m_wipe_tower_pos,m_angle_deg); }
+	const WipeTower::xy  pos_rotated() const { return m_current_pos.rotate(m_wipe_tower_pos,m_angle_deg); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
 
 	// Extrude with an explicitely provided amount of extrusion.
@@ -87,7 +87,6 @@ public:
 		double len = sqrt(dx*dx+dy*dy);
 		
 		// For rotated wipe tower, transform position to printer coordinates
-		//Q zavadet nove promenne? rotace o zaporny uhel?
 		WipeTower::xy rotated_current_pos( m_current_pos.rotate(m_wipe_tower_pos,m_angle_deg) );
 		WipeTower::xy rot( WipeTower::xy(x,y).rotate(m_wipe_tower_pos,m_angle_deg ) );
 						
@@ -491,8 +490,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	result.gcode   	  	= writer.gcode();
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
-	result.start_pos  	= writer.start_pos();
-	result.end_pos 	  	= writer.printer_pos();
+	result.start_pos  	= writer.start_pos_rotated();
+	result.end_pos 	  	= writer.pos_rotated();
 	return result;
 }
 
@@ -627,8 +626,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	result.gcode   	  	= writer.gcode();
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
-	result.start_pos  	= writer.start_pos();
-	result.end_pos 	  	= writer.printer_pos();
+	result.start_pos  	= writer.start_pos_rotated();
+	result.end_pos 	  	= writer.pos_rotated();
 	return result;
 }
 
@@ -728,8 +727,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 	result.gcode   	  	= writer.gcode();
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
-	result.start_pos  	= writer.start_pos();
-	result.end_pos 	  	= writer.printer_pos();
+	result.start_pos  	= writer.start_pos_rotated();
+	result.end_pos 	  	= writer.pos_rotated();
 	return result;
 }
 
@@ -1049,8 +1048,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 	result.gcode   	  	= writer.gcode();
 	result.elapsed_time = writer.elapsed_time();
 	result.extrusions 	= writer.extrusions();
-	result.start_pos 	= writer.start_pos();
-	result.end_pos 	  	= writer.printer_pos();
+	result.start_pos 	= writer.start_pos_rotated();
+	result.end_pos 	  	= writer.pos_rotated();
 	return result;
 }
 

From c34fd10e23eef14c083fcd14f55e36acda2afa84 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 5 Dec 2017 11:25:38 +0100
Subject: [PATCH 006/118] Wipe tower rotation around center point

---
 xs/src/libslic3r/GCode/WipeTower.hpp        | 11 ++--
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 65 +++++++++++----------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  7 ++-
 xs/src/libslic3r/PrintConfig.cpp            |  2 +-
 xs/src/slic3r/GUI/3DScene.cpp               |  7 ++-
 5 files changed, 52 insertions(+), 40 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index 8a97333de..c5ce9b727 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -25,12 +25,15 @@ public:
 		bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
 		
 		// Rotate the point around given point about given angle (in degrees)
-		xy rotate(const xy& origin, float angle) const {
+		// shifts the result so that point of rotation is in the middle of the tower
+		xy rotate(const xy& origin, float width, float depth, float angle) const {
 			xy out(0,0);
+			float temp_x = x - width / 2.f;
+			float temp_y = y - depth / 2.f;
 			angle *= M_PI/180.;
-			out.x=(x-origin.x) * cos(angle)  -  (y-origin.y) * sin(angle);
-			out.y=(x-origin.x) * sin(angle)  +  (y-origin.y) * cos(angle);
-			return out+origin;
+			out.x += (temp_x - origin.x) * cos(angle)  -  (temp_y - origin.y) * sin(angle);
+			out.y += (temp_x - origin.x) * sin(angle)  +  (temp_y - origin.y) * cos(angle);
+			return out + origin;
 		}
 		
 		float x;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index cec3e0cf9..de772060d 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -15,7 +15,6 @@
 #endif
 
 
-
 namespace Slic3r
 {
 
@@ -50,8 +49,8 @@ public:
 	Writer& 			 set_extrusion_flow(float flow)
 		{ m_extrusion_flow = flow; return *this; }
 		
-	Writer&				 set_rotation(WipeTower::xy pos,float angle)
-		{ m_wipe_tower_pos = pos; m_angle_deg = angle; return (*this); }
+	Writer&				 set_rotation(WipeTower::xy pos,float width,float depth,float angle)
+		{ m_wipe_tower_pos = pos; m_wipe_tower_depth = depth; m_wipe_tower_width = width; m_angle_deg = angle; return (*this); }
 
 	// 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
@@ -71,8 +70,8 @@ public:
 	float                x()     const { return m_current_pos.x; }
 	float                y()     const { return m_current_pos.y; }
 	const WipeTower::xy& pos()   const { return m_current_pos; }
-	const WipeTower::xy  start_pos_rotated() const { return m_start_pos.rotate(m_wipe_tower_pos,m_angle_deg); }
-	const WipeTower::xy  pos_rotated() const { return m_current_pos.rotate(m_wipe_tower_pos,m_angle_deg); }
+	const WipeTower::xy	 start_pos_rotated() const { return m_start_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
+	const WipeTower::xy pos_rotated() const { return m_current_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
 
 	// Extrude with an explicitely provided amount of extrusion.
@@ -87,10 +86,9 @@ public:
 		double len = sqrt(dx*dx+dy*dy);
 		
 		// For rotated wipe tower, transform position to printer coordinates
-		WipeTower::xy rotated_current_pos( m_current_pos.rotate(m_wipe_tower_pos,m_angle_deg) );
-		WipeTower::xy rot( WipeTower::xy(x,y).rotate(m_wipe_tower_pos,m_angle_deg ) );
-						
-		
+		WipeTower::xy rotated_current_pos(m_current_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
+		WipeTower::xy rot(WipeTower::xy(x, y).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
+
 		if (! m_preview_suppressed && e > 0.f && len > 0.) {
 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
 			// This is left zero if it is a travel move.
@@ -160,7 +158,7 @@ public:
 		m_gcode += "\n";
 		return *this;
 	}
-
+ 
 	// Derectract while moving in the X direction.
 	// If |x| > 0, the feed rate relates to the x distance,
 	// otherwise the feed rate relates to the e distance.
@@ -299,6 +297,8 @@ private:
 	const double  m_filament_area = 0.25*M_PI*1.75*1.75;
 	float   	  m_angle_deg = 0;
 	WipeTower::xy m_wipe_tower_pos;
+	float 		  m_wipe_tower_width;
+	float		  m_wipe_tower_depth;
 
 	std::string   set_format_X(float x) {
 		char buf[64];
@@ -543,18 +543,18 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 
 	PrusaMultiMaterial::Writer writer;
 	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(this->m_wipe_tower_pos,m_wipe_tower_rotation_angle)
-		  .append(";--------------------\n"
-			 	  "; CP TOOLCHANGE START\n")
-		  .comment_with_value(" toolchange #", m_num_tool_changes)
-		  .comment_material(m_material[m_current_tool])
-		  .append(";--------------------\n")
-		  .speed_override(100);
+		.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)
+		.append(";--------------------\n"
+				"; CP TOOLCHANGE START\n")
+		.comment_with_value(" toolchange #", m_num_tool_changes)
+		.comment_material(m_material[m_current_tool])
+		.append(";--------------------\n")
+		.speed_override(100);
 
-    xy initial_position = ((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) + 
+	xy initial_position = ((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) + 
     	xy(m_perimeter_width, ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width);
 
 	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
@@ -640,11 +640,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 
 	PrusaMultiMaterial::Writer writer;
 	writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
-		  // Let the writer know the current Z position as a base for Z-hop.
+		// Let the writer know the current Z position as a base for Z-hop.
 		  .set_z(m_z_pos)
 		  .set_layer_height(m_layer_height)
 		  .set_initial_tool(m_current_tool)
-		  .set_rotation(this->m_wipe_tower_pos,m_wipe_tower_rotation_angle)
+		  .set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
+		  //.set_offset(xy(-))
 		  .append(
 			";-------------------------------------\n"
 			"; CP WIPE TOWER FIRST LAYER BRIM START\n");
@@ -866,7 +867,7 @@ void WipeTowerPrusaMM::toolchange_Load(
 	writer.extrude(xr, writer.y(), 1600);
 	bool   colorInit = false;
 	size_t pass = colorInit ? 1 : 2;
-	float  dy   = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * 0.85f;
+	float  dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * 0.85f;
 	for (int i = 0; i < pass; ++ i) {
 		writer.travel (xr, writer.y() + dy, 7200);
 		writer.extrude(xl, writer.y(), 		2200);
@@ -930,14 +931,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 
 	PrusaMultiMaterial::Writer writer;
 	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(this->m_wipe_tower_pos,m_wipe_tower_rotation_angle)
-		  .append(";--------------------\n"
-				  "; CP EMPTY GRID START\n")
-		  // m_num_layer_changes is incremented by set_z, so it is 1 based.
-		  .comment_with_value(" layer #", m_num_layer_changes - 1);
+		.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)
+		.append(";--------------------\n"
+				"; CP EMPTY GRID START\n")
+		// m_num_layer_changes is incremented by set_z, so it is 1 based.
+		.comment_with_value(" layer #", m_num_layer_changes - 1);
 
 	// Slow down on the 1st layer.
 	float speed_factor = m_is_first_layer ? 0.5f : 1.f;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index fd9c55cb7..8f3e7ccaf 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -9,7 +9,6 @@
 #include "WipeTower.hpp"
 
 
-
 namespace Slic3r
 {
 
@@ -86,6 +85,9 @@ public:
 		// Is this the last layer of the waste tower?
 		bool   is_last_layer)
 	{
+		if (is_first_layer)
+			m_wipe_tower_depth = m_wipe_area * max_tool_changes; // tower depth (y-range) of the bottom
+
 		m_z_pos 				= print_z;
 		m_layer_height			= layer_height;
 		m_max_color_changes 	= max_tool_changes;
@@ -151,6 +153,8 @@ private:
 	xy     			m_wipe_tower_pos;
 	// Width of the wipe tower.
 	float  			m_wipe_tower_width;
+	// Depth of the wipe tower (wipe_area * max_color_changes at the base)
+	float			m_wipe_tower_depth;
 	// Per color Y span.
 	float  			m_wipe_area;
 	// Wipe tower rotation angle in degrees (with respect to x axis
@@ -171,6 +175,7 @@ private:
 	float  			m_retract		 = 4.f;
 	// Width of an extrusion line, also a perimeter spacing for 100% infill.
 	float  			m_perimeter_width = 0.5f;
+	
 	// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
 	float  			m_extrusion_flow  = 0.029f;
 
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 65ae56961..b561388fd 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1710,7 +1710,7 @@ PrintConfigDef::PrintConfigDef()
 
     def = this->add("wipe_tower_rotation_angle", coFloat);
     def->label = "Wipe tower rotation angle";
-    def->tooltip = "Wipe tower rotation angle with respect to x-asis ";
+    def->tooltip = "Wipe tower rotation angle with respect to x-axis ";
     def->sidetext = "degrees";
     def->cli = "wipe-tower-rotation-angle=f";
     def->default_value = new ConfigOptionFloat(0.);
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index bb4183bdf..83432446a 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -306,8 +306,11 @@ int GLVolumeCollection::load_wipe_tower_preview(
     float color[4] = { 1.0f, 1.0f, 0.0f, 0.5f };
     this->volumes.emplace_back(new GLVolume(color));
     GLVolume &v = *this->volumes.back();
-    auto mesh = make_cube(width, depth, height);
-	mesh.rotate_z(rotation_angle*M_PI/180.);
+    auto mesh = make_cube(width, depth, height);    
+    mesh.translate(-width/2.f,-depth/2.f,0.f);    
+    Point origin_of_rotation(0.f,0.f);
+    mesh.rotate(rotation_angle,&origin_of_rotation);
+
 	v.indexed_vertex_array.load_mesh_flat_shading(mesh);
 	v.origin = Pointf3(pos_x, pos_y, 0.);
     // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().

From 87c67636dfecc8612dcc4825708560fc1fa57b62 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 7 Dec 2017 11:59:14 +0100
Subject: [PATCH 007/118] Extrusion flow calculation based on nozzle diameter
 etc

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 11 +++++-----
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 23 +++++++++++++++++----
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index de772060d..03a5f0b15 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -406,7 +406,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 		float e_length = this->tool_change(0, false, PURPOSE_EXTRUDE).total_extrusion_length_in_plane();
 		// Shrink wipe_area by the amount of extrusion extruded by the finish_layer().
 		// Y stepping of the wipe extrusions.
-		float dy = m_perimeter_width * 0.8f;
+		float dy = m_line_width;
 		// Number of whole wipe lines, that would be extruded to wipe as much material as the finish_layer().
 		// Minimum wipe area is 5mm wide.
 		//FIXME calculate the purge_lines_width precisely.
@@ -453,7 +453,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 				if (last_wipe_inside_wipe_tower) {
 					// Shrink the last wipe area to the area of the other purge areas,
 					// remember the last initial wipe width to be purged into the 1st layer of the wipe tower.
-					this->m_initial_extra_wipe = std::max(0.f, wipe_area - (y_end + 0.5f * 0.85f * m_perimeter_width - cleaning_box.ld.y));
+					this->m_initial_extra_wipe = std::max(0.f, wipe_area - (y_end + 0.5f * m_line_width - cleaning_box.ld.y));
 					cleaning_box.lu.y -= this->m_initial_extra_wipe;
 					cleaning_box.ru.y -= this->m_initial_extra_wipe;
 				}
@@ -524,7 +524,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 			}
 			// Shrink wipe_area by the amount of extrusion extruded by the finish_layer().
 			// Y stepping of the wipe extrusions.
-			float dy = m_perimeter_width * 0.8f;
+			float dy = m_line_width;
 			// Number of whole wipe lines, that would be extruded to wipe as much material as the finish_layer().
 			float num_lines_extruded = floor(e_length / m_wipe_tower_width);
 			// Minimum wipe area is 5mm wide.
@@ -645,7 +645,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 		  .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_offset(xy(-))
 		  .append(
 			";-------------------------------------\n"
 			"; CP WIPE TOWER FIRST LAYER BRIM START\n");
@@ -867,7 +866,7 @@ void WipeTowerPrusaMM::toolchange_Load(
 	writer.extrude(xr, writer.y(), 1600);
 	bool   colorInit = false;
 	size_t pass = colorInit ? 1 : 2;
-	float  dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * 0.85f;
+	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
 	for (int i = 0; i < pass; ++ i) {
 		writer.travel (xr, writer.y() + dy, 7200);
 		writer.extrude(xl, writer.y(), 		2200);
@@ -896,7 +895,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	float wipe_speed_inc = 50.f;
 	float wipe_speed_max = 4800.f;
 	// Y increment per wipe line.
-	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * 0.8f;
+	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
 	for (bool p = true; 
 		// Next wipe line fits the cleaning box.
 		((m_current_shape == SHAPE_NORMAL) ?
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 8f3e7ccaf..80ccb7ee9 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -8,6 +8,18 @@
 
 #include "WipeTower.hpp"
 
+// Following is used to calculate extrusion flow - should be taken from config in future
+constexpr float Filament_Diameter = 1.75f; // filament diameter in mm
+constexpr float Nozzle_Diameter = 0.4f;	// nozzle diameter in mm
+// desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
+constexpr float Width_To_Nozzle_Ratio = 1.f;
+// m_perimeter_width was hardcoded until now as 0.5 (for 0.4 nozzle and 0.2 layer height)
+// Konst = 1.25 implies same result
+constexpr float Konst = 1.25f;
+constexpr float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Konst;
+
+
+
 
 namespace Slic3r
 {
@@ -63,7 +75,7 @@ public:
 	
 	// _zHop - z hop value in mm
 	void set_zhop(float zhop) { m_zhop = zhop; }
-
+	
 	// Set the extruder properties.
 	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp)
 	{
@@ -101,9 +113,12 @@ public:
 		// Extrusion rate for an extrusion aka perimeter width 0.35mm.
 		// Clamp the extrusion height to a 0.2mm layer height, independent of the nozzle diameter.
 //		m_extrusion_flow = std::min(0.2f, layer_height) * 0.145f;
-
 		// Use a strictly
-		m_extrusion_flow = layer_height * 0.145f;
+		//m_extrusion_flow = layer_height * 0.145f;
+		
+		// Calculates extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height
+		m_extrusion_flow = 4.f * layer_height * ( Width_To_Nozzle_Ratio * Nozzle_Diameter - layer_height * (1-M_PI/4.f)) /
+					   		(M_PI * pow(Filament_Diameter,2.f));
 	}
 
 	// Return the wipe tower position.
@@ -174,7 +189,7 @@ private:
 	float  			m_zhop 			 = 0.5f;
 	float  			m_retract		 = 4.f;
 	// Width of an extrusion line, also a perimeter spacing for 100% infill.
-	float  			m_perimeter_width = 0.5f;
+	float  			m_line_width = Nozzle_Diameter * Width_To_Nozzle_Ratio;
 	
 	// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
 	float  			m_extrusion_flow  = 0.029f;

From b7a326a550131ba91e4b9651e45d12808d52425e Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 21 Dec 2017 13:28:26 +0100
Subject: [PATCH 008/118] First attempts of  variable wipe implementation,
 sparse infill modified to sawtooth

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 248 ++++++++++++++++----
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 109 +++++++--
 xs/src/libslic3r/Print.cpp                  |  28 ++-
 3 files changed, 310 insertions(+), 75 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 03a5f0b15..3890a3626 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -49,8 +49,8 @@ public:
 	Writer& 			 set_extrusion_flow(float flow)
 		{ m_extrusion_flow = flow; return *this; }
 		
-	Writer&				 set_rotation(WipeTower::xy pos,float width,float depth,float angle)
-		{ m_wipe_tower_pos = pos; m_wipe_tower_depth = depth; m_wipe_tower_width = width; m_angle_deg = angle; return (*this); }
+	Writer&				 set_rotation(WipeTower::xy& pos, float width, float depth, float angle)
+		{ m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
 
 	// 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
@@ -87,17 +87,17 @@ public:
 		
 		// For rotated wipe tower, transform position to printer coordinates
 		WipeTower::xy rotated_current_pos(m_current_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
-		WipeTower::xy rot(WipeTower::xy(x, y).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
+		WipeTower::xy rot(WipeTower::xy(x,y).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
 
 		if (! m_preview_suppressed && e > 0.f && len > 0.) {
 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
 			// This is left zero if it is a travel move.
 			float width = float(double(e) * m_filament_area / (len * m_layer_height));
 			// Correct for the roundings of a squished extrusion.
-			width += float(m_layer_height * (1. - M_PI / 4.));
+			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";
@@ -401,7 +401,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	float wipe_area = m_wipe_area;
 	// Calculate the amount of wipe over the wipe tower brim following the prime, decrease wipe_area
 	// with the amount of material extruded over the brim.
-	{
+	// DOESN'T MAKE SENSE NOW, wipe tower dimensions are still unknown at this point
+	/*{
 		// Simulate the brim extrusions, summ the length of the extrusion.
 		float e_length = this->tool_change(0, false, PURPOSE_EXTRUDE).total_extrusion_length_in_plane();
 		// Shrink wipe_area by the amount of extrusion extruded by the finish_layer().
@@ -414,7 +415,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 		wipe_area = std::max(5.f, m_wipe_area - float(floor(e_length / m_wipe_tower_width)) * dy - purge_lines_width);
 	}
 
-	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
+	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);*/
 	this->m_num_layer_changes 	= 0;
 	this->m_current_tool 		= tools.front();
 
@@ -422,7 +423,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
     // Due to the XYZ calibration, this working space may shrink slightly from all directions,
     // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
 //	box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
-	box_coordinates cleaning_box(xy(5.f, 0.f), m_wipe_tower_width, wipe_area);
+	//FIXME: set the width properly
+	constexpr float prime_section_width = 60.f;
+	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, wipe_area);
 
 	PrusaMultiMaterial::Writer writer;
 	writer.set_extrusion_flow(m_extrusion_flow)
@@ -465,7 +468,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 				toolchange_Unload(writer, cleaning_box, m_material[m_current_tool], m_first_layer_temperature[tools[idx_tool+1]]);
 				// Save the y end of the non-last priming area.
 				y_end = writer.y();
-			    cleaning_box.translate(m_wipe_tower_width, 0.f);
+				cleaning_box.translate(prime_section_width, 0.f);
 				writer.travel(cleaning_box.ld, 7200);
 			}
 		    ++ m_num_tool_changes;
@@ -482,7 +485,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 				  "\n\n");
 
 	// Force m_idx_tool_change_in_layer to -1, so that tool_change() will know to extrude the wipe tower brim.
-	m_idx_tool_change_in_layer = (unsigned int)(-1);
+	///m_idx_tool_change_in_layer = (unsigned int)(-1);
+	m_print_brim = true;
 
 	ToolChangeResult result;
 	result.print_z 	  	= this->m_z_pos;
@@ -501,13 +505,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	// or there must be a nonzero wipe tower partitions available.
 //	assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0);
 
-	if (m_idx_tool_change_in_layer == (unsigned int)(-1)) {
+	if ( m_print_brim ) {
+	///if (m_idx_tool_change_in_layer == (unsigned int)(-1)) {
 		// First layer, prime the extruder.
 		return toolchange_Brim(purpose);
 	}
-
-	float wipe_area = m_wipe_area;
-	if (++ m_idx_tool_change_in_layer < (unsigned int)m_max_color_changes && last_in_layer) {
+	
+	float wipe_area = 0.f;
+	/*if (++ m_idx_tool_change_in_layer < (unsigned int)m_max_color_changes && last_in_layer) {
 		// This tool_change() call will be followed by a finish_layer() call.
 		// Try to shrink the wipe_area to save material, as less than usual wipe is required
 		// if this step is foolowed by finish_layer() extrusions wiping the same extruder.
@@ -534,11 +539,20 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 				break;
 			}
 		}
-	}
+	}*/
 
+	if (tool != (unsigned int)(-1)) {
+		for (const auto &b : m_layer_info->tool_changes)
+			if ( b.new_tool == tool )
+				wipe_area = b.required_depth;
+	}
+	else {
+		// Otherwise we are going to Unload only. And m_layer_info would be invalid.
+	}
+	
 	box_coordinates cleaning_box(
 		m_wipe_tower_pos + xy(0.f, m_current_wipe_start_y + 0.5f * m_perimeter_width),
-		m_wipe_tower_width, 
+		m_wipe_tower_width,
 		wipe_area - m_perimeter_width);
 
 	PrusaMultiMaterial::Writer writer;
@@ -546,7 +560,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.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_rotation(m_wipe_tower_pos,m_wipe_tower_width,m_wipe_tower_depth,m_wipe_tower_rotation_angle)
 		.append(";--------------------\n"
 				"; CP TOOLCHANGE START\n")
 		.comment_with_value(" toolchange #", m_num_tool_changes)
@@ -636,16 +650,16 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 	const box_coordinates wipeTower_box(
 		m_wipe_tower_pos,
 		m_wipe_tower_width,
-		m_wipe_area * float(m_max_color_changes) - m_perimeter_width / 2);
+		m_wipe_tower_depth - m_perimeter_width / 2);
 
 	PrusaMultiMaterial::Writer writer;
 	writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
 		// Let the writer know the current Z position as a base for Z-hop.
-		  .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)
-		  .append(
+		.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)
+		.append(
 			";-------------------------------------\n"
 			"; CP WIPE TOWER FIRST LAYER BRIM START\n");
 
@@ -718,7 +732,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 				      ";-----------------------------------\n");
 
 		// Mark the brim as extruded.
-		m_idx_tool_change_in_layer = 0;
+		///m_idx_tool_change_in_layer = 0;
+		m_print_brim = false;
 	}
 
 	ToolChangeResult result;
@@ -896,11 +911,12 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	float wipe_speed_max = 4800.f;
 	// Y increment per wipe line.
 	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
+
 	for (bool p = true; 
-		// Next wipe line fits the cleaning box.
+		// Next wipe line fits the cleaning box (and not forgetting about border)
 		((m_current_shape == SHAPE_NORMAL) ?
-			(writer.y() <= cleaning_box.lu.y - m_perimeter_width) :
-			(writer.y() >= cleaning_box.ld.y + m_perimeter_width));
+			(writer.y() <= cleaning_box.lu.y - 1.5f * m_perimeter_width) :
+			(writer.y() >= cleaning_box.ld.y + 1.5f * m_perimeter_width));
 		p = ! p)
 	{
 		wipe_speed = std::min(wipe_speed_max, wipe_speed + wipe_speed_inc);
@@ -922,6 +938,9 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	writer.set_extrusion_flow(m_extrusion_flow);
 }
 
+
+
+
 WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 {
 	// This should only be called if the layer is not finished yet.
@@ -942,17 +961,20 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 	// Slow down on the 1st layer.
 	float speed_factor = m_is_first_layer ? 0.5f : 1.f;
 
+	float current_depth = m_wipe_tower_depth;
+	if (m_is_first_layer)
+		current_depth = m_wipe_tower_depth;
+	else 
+		current_depth = m_layer_info->depth;
+
+
 	box_coordinates fill_box(m_wipe_tower_pos + xy(0.f, m_current_wipe_start_y),
-		m_wipe_tower_width, float(m_max_color_changes) * m_wipe_area - m_current_wipe_start_y);
+		m_wipe_tower_width, current_depth - m_current_wipe_start_y);
 	fill_box.expand(0.f, - 0.5f * m_perimeter_width);
-	{
-		float firstLayerOffset = 0.f;
-		fill_box.ld.y += firstLayerOffset;
-		fill_box.rd.y += firstLayerOffset;
-	}
+	
 
 	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		if (m_idx_tool_change_in_layer == 0) {
+		if ( m_layer_info->tool_changes.size() == 0) {
 			// There were no tool changes at all in this layer.
 			writer.retract(m_retract * 1.5f, 3600)
 				  // Jump with retract to fill_box.ld + a random shift in +x.
@@ -992,12 +1014,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 			box.rd.y  = box.ld.y;
 			int   nsteps = int(floor((box.lu.y - box.ld.y) / (2. * (1.0 * m_perimeter_width))));
 			float step   = (box.lu.y - box.ld.y) / nsteps;
-			for (size_t i = 0; i < nsteps; ++ i) {
-				writer.extrude(box.ld.x, writer.y() + 0.5f * step);
-				writer.extrude(box.rd.x, writer.y());
-				writer.extrude(box.rd.x, writer.y() + 0.5f * step);
-				writer.extrude(box.ld.x, writer.y());
-			}
+			//FIXME:
+			if (nsteps >= 0)
+				for (size_t i = 0; i < nsteps; ++i)	{
+					writer.extrude(box.ld.x, writer.y() + 0.5f * step);
+					writer.extrude(box.rd.x, writer.y());
+					writer.extrude(box.rd.x, writer.y() + 0.5f * step);
+					writer.extrude(box.ld.x, writer.y());
+				}
 		} else {
 			// Extrude a sparse infill to support the material to be printed above.
 
@@ -1008,10 +1032,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 				  .extrude(fill_box.lu + xy(m_perimeter_width * 6, - m_perimeter_width))
 				  .extrude(fill_box.ld + xy(m_perimeter_width * 6,   m_perimeter_width));
 
-			if (fill_box.lu.y - fill_box.ld.y > 4.f) {
-				// Extrude three zig-zags.
-				float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f;
-				for (size_t i = 0; i < 3; ++ i) {
+			/*if (fill_box.lu.y - fill_box.ld.y > 4.f) {
+				const float max_bridge_distance = 10.f; // in mm
+				size_t zig_zags_num = int((m_wipe_tower_width - m_perimeter_width * 12.f) / ( max_bridge_distance * 2.f ) ) + 1 ;
+				float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / (zig_zags_num * 4);
+				for (size_t i = 0; i < zig_zags_num; ++ i) {
 					writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width * 8, 3200 * speed_factor);
 					writer.extrude(writer.x()       , fill_box.lu.y - m_perimeter_width * 8);
 					writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width    );
@@ -1020,12 +1045,42 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 					writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width    );
 				}
 			}
+			// Extrude an inverse U at the left of the region.
+			writer.extrude(fill_box.ru + xy(-m_perimeter_width * 6, -m_perimeter_width), 2900 * speed_factor)
+				  .extrude(fill_box.ru + xy(-m_perimeter_width * 3, -m_perimeter_width))
+				  .extrude(fill_box.rd + xy(-m_perimeter_width * 3, m_perimeter_width))
+				  .extrude(fill_box.rd + xy(-m_perimeter_width, m_perimeter_width));*/
+
+			const float dy = (fill_box.lu.y - fill_box.ld.y - 2.f * m_perimeter_width);
+			if (dy > m_perimeter_width)
+			{				
+				const float max_bridge_distance = 10.f; // in mm
+				const size_t zig_zags_num = (fill_box.rd.x - fill_box.ld.x - m_perimeter_width * 12.f) / max_bridge_distance;
+				const float step = (fill_box.rd.x - fill_box.ld.x - m_perimeter_width * 12.f) / (float)zig_zags_num;
+
+				float offsety = std::max(0.f,  dy - m_last_infill_tan * (step - m_perimeter_width));
+				if (offsety < m_last_infill_tan * m_perimeter_width + WT_EPSILON || offsety > dy / 2.f)
+					offsety = 0.f;
+				float offsetx = ( offsety != 0 ? 0 : std::max(0.f, dy / m_last_infill_tan > step / 2.f ? step - dy / m_last_infill_tan : 0.f ) );
+				
+				for (size_t i = 0; i < zig_zags_num; ++i)
+				{
+					writer.extrude(writer.x() + offsetx, writer.y(), 3200 * speed_factor);
+					if ( offsety != 0 ) {
+						writer.extrude(writer.x() + m_perimeter_width, writer.y());
+						writer.extrude(writer.x(), writer.y() + offsety);
+					}
+					writer.extrude(writer.x() + step - offsetx - (offsety==0 ? 0 : m_perimeter_width), fill_box.lu.y - m_perimeter_width);
+					writer.extrude(writer.x() , fill_box.ld.y + m_perimeter_width);
+				}
+				m_last_infill_tan = (dy - offsety) / (step - offsetx - (offsety==0 ? 0 : m_perimeter_width));
+			}
 
 			// Extrude an inverse U at the left of the region.
-			writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor)
-				  .extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width))
-				  .extrude(fill_box.rd + xy(- m_perimeter_width * 3,   m_perimeter_width))
-				  .extrude(fill_box.rd + xy(- m_perimeter_width,       m_perimeter_width));
+			writer.extrude(fill_box.rd + xy(-m_perimeter_width * 6, m_perimeter_width), 2900 * speed_factor)
+				  .extrude(fill_box.rd + xy(-m_perimeter_width * 3, m_perimeter_width))
+				  .extrude(fill_box.ru + xy(-m_perimeter_width * 3, -m_perimeter_width))
+				  .extrude(fill_box.ru + xy(-m_perimeter_width, -m_perimeter_width));
 		}
 
 		if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
@@ -1039,7 +1094,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 				      ";------------------\n\n\n\n\n\n\n");
 
 		// Indicate that this wipe tower layer is fully covered.
-	    m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;
+		m_current_wipe_start_y = m_wipe_tower_depth;
+	    ///m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;
+		
 	}
 
 	ToolChangeResult result;
@@ -1053,4 +1110,97 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 	return result;
 }
 
+// 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)
+{
+	assert(m_plan.back().z <= z_par);	// refuse to add a layer below the last one
+	
+	// volumes in mm^3 required for ramming
+	// hardcoded so far, in future should be calculated by integrating the speed/time curve
+	const std::vector<float> ram_volumes = {22,22,22,22};
+	
+	// volumes in mm^3 required for wipe: {{from 0 to ...},{from 1 to ...},{from 2 to ...},{from 3 to ...}}, usage [from][to]
+	const std::vector<std::vector<float>> wipe_volumes = {{  0, 40, 60, 80},
+														  {100,  0,120,100},
+														  { 80,107,  0,110},
+														  { 60, 40, 60,  0}};
+
+	float depth = (wipe_volumes[old_tool][new_tool]) / (m_extrusion_flow * Filament_Area); // length of extrusion
+	depth = floor(depth / m_wipe_tower_width + 1);	// number of lines to extrude
+	depth += 6; // reserved for ramming
+	depth += 2; // reserved for loading
+	depth -= 2; // we will also print borders
+	depth *= m_perimeter_width; // conversion to distance
+
+	if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan along with the first toolchange
+		m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
+
+	if ( old_tool != new_tool )	{
+		if (!m_plan_brim_finished) {	// this toolchange prints brim, we need it in m_plan, but not to count its depth
+			m_plan_brim_finished = true;
+			depth = 0.f;
+		}
+		m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth));
+	}
+
+	
+
+	// Calculate m_wipe_tower (maximum depth for all the layers) and propagate depths downwards
+	// Could be only called when a layer is changed, but do not forget about the last one
+	float this_layer_depth = m_plan.back().toolchanges_depth();
+	m_plan.back().depth = this_layer_depth;
+	
+	if (this_layer_depth > m_wipe_tower_depth)
+		m_wipe_tower_depth = this_layer_depth;
+	for (int i = m_plan.size() - 2; i >= 0 && m_plan[i].depth < this_layer_depth; i-- ) {
+		if ( this_layer_depth - m_plan[i].depth < min_layer_difference && !m_plan[i].tool_changes.empty())
+			m_plan[i].tool_changes.back().required_depth += this_layer_depth - m_plan[i].depth;
+		m_plan[i].depth = this_layer_depth;
+	}
+}
+
+
+// Processes vector m_plan and calls respective function to generate G-code for the wipe tower
+// Resulting ToolChangeResults are appended into container passed by "result" reference
+void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &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);
+
+		for (const auto &toolchange : layer.tool_changes)
+			layer_result.emplace_back(tool_change(toolchange.new_tool, false, WipeTower::PURPOSE_EXTRUDE));
+
+		if (! layer_finished()) {
+			layer_result.emplace_back(finish_layer(WipeTower::PURPOSE_EXTRUDE));
+			if (layer_result.size() > 1) {
+				// Merge the two last tool changes into one.
+				WipeTower::ToolChangeResult &tc1 = layer_result[layer_result.size() - 2];
+				WipeTower::ToolChangeResult &tc2 = layer_result.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;
+				tc1.extrusions.insert(tc1.extrusions.end(), tc2.extrusions.begin(), tc2.extrusions.end());
+				tc1.end_pos = tc2.end_pos;
+				layer_result.pop_back();
+			}			
+		}
+		else
+			m_last_infill_tan = 1000.f; //if the layer was dense, no worries about infill continuity on the next
+
+		result.emplace_back(std::move(layer_result));
+		m_is_first_layer = false;
+	}
+}
+
+
+
 }; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 80ccb7ee9..275b2f237 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -9,15 +9,19 @@
 #include "WipeTower.hpp"
 
 // Following is used to calculate extrusion flow - should be taken from config in future
-constexpr float Filament_Diameter = 1.75f; // filament diameter in mm
+constexpr float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament diameter in mm
 constexpr float Nozzle_Diameter = 0.4f;	// nozzle diameter in mm
 // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
 constexpr float Width_To_Nozzle_Ratio = 1.f;
 // m_perimeter_width was hardcoded until now as 0.5 (for 0.4 nozzle and 0.2 layer height)
 // Konst = 1.25 implies same result
 constexpr float Konst = 1.25f;
+
+// m_perimeter_width is used in plan_toolchange - take care of proper initialization value when changing to variable
 constexpr float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Konst;
 
+constexpr float WT_EPSILON = 1e-3f;
+constexpr float min_layer_difference = 2.f;
 
 
 
@@ -75,7 +79,8 @@ public:
 	
 	// _zHop - z hop value in mm
 	void set_zhop(float zhop) { m_zhop = zhop; }
-	
+
+
 	// Set the extruder properties.
 	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp)
 	{
@@ -84,32 +89,39 @@ public:
 		m_first_layer_temperature[idx] = first_layer_temp;
 	}
 
+
+	// Setter for 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);
+
+	void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
+
 	// Switch to a next layer.
 	virtual void set_layer(
 		// Print height of this layer.
-		float  print_z,
-		// Layer height, used to calculate extrusion the rate. 
-		float  layer_height, 
+		float print_z,
+		// Layer height, used to calculate extrusion the rate.
+		float layer_height,
 		// Maximum number of tool changes on this layer or the layers below.
-		size_t max_tool_changes, 
+		size_t max_tool_changes,
 		// Is this the first layer of the print? In that case print the brim first.
-		bool   is_first_layer,
+		bool is_first_layer,
 		// Is this the last layer of the waste tower?
-		bool   is_last_layer)
+		bool is_last_layer)
 	{
-		if (is_first_layer)
-			m_wipe_tower_depth = m_wipe_area * max_tool_changes; // tower depth (y-range) of the bottom
-
 		m_z_pos 				= print_z;
 		m_layer_height			= layer_height;
-		m_max_color_changes 	= max_tool_changes;
 		m_is_first_layer 		= is_first_layer;
-		m_is_last_layer			= is_last_layer;
 		// Start counting the color changes from zero. Special case: -1 - extrude a brim first.
-		m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
+		///m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
+		m_print_brim = is_first_layer;
 		m_current_wipe_start_y  = 0.f;
 		m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
 		++ m_num_layer_changes;
+
+
+		///m_max_color_changes 	= max_tool_changes;
+		///m_is_last_layer			= is_last_layer;
 		// Extrusion rate for an extrusion aka perimeter width 0.35mm.
 		// Clamp the extrusion height to a 0.2mm layer height, independent of the nozzle diameter.
 //		m_extrusion_flow = std::min(0.2f, layer_height) * 0.145f;
@@ -117,8 +129,14 @@ public:
 		//m_extrusion_flow = layer_height * 0.145f;
 		
 		// Calculates extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height
-		m_extrusion_flow = 4.f * layer_height * ( Width_To_Nozzle_Ratio * Nozzle_Diameter - layer_height * (1-M_PI/4.f)) /
-					   		(M_PI * pow(Filament_Diameter,2.f));
+		m_extrusion_flow = extrusion_flow(layer_height);
+
+		m_layer_info = nullptr;
+		for (auto &a : m_plan)
+			if ( a.z > print_z - WT_EPSILON && a.z < print_z + WT_EPSILON ) {
+				m_layer_info = &a;
+				break;
+			}
 	}
 
 	// Return the wipe tower position.
@@ -151,8 +169,15 @@ public:
 	// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
 	// the wipe tower has been completely covered by the tool change extrusions,
 	// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
-	virtual bool 			 layer_finished() const
-		{ return m_idx_tool_change_in_layer == m_max_color_changes; }
+	virtual bool 			 layer_finished() const {
+		
+		if (m_is_first_layer) {
+			return (m_wipe_tower_depth - WT_EPSILON < m_current_wipe_start_y);
+		}
+		else
+			return (m_layer_info->depth - WT_EPSILON < m_current_wipe_start_y);
+	}
+
 
 private:
 	WipeTowerPrusaMM();
@@ -169,7 +194,7 @@ private:
 	// Width of the wipe tower.
 	float  			m_wipe_tower_width;
 	// Depth of the wipe tower (wipe_area * max_color_changes at the base)
-	float			m_wipe_tower_depth;
+	float			m_wipe_tower_depth = 0.f;
 	// Per color Y span.
 	float  			m_wipe_area;
 	// Wipe tower rotation angle in degrees (with respect to x axis
@@ -205,7 +230,8 @@ private:
 	// Tool change change counter for the output statistics.
 	unsigned int 	m_num_tool_changes = 0;
 	// Layer change counter in this layer. Counting up to m_max_color_changes.
-	unsigned int 	m_idx_tool_change_in_layer = 0;
+	///unsigned int 	m_idx_tool_change_in_layer = 0;
+	bool m_print_brim = true;
 	// A fill-in direction (positive Y, negative Y) alternates with each layer.
 	wipe_shape   	m_current_shape = SHAPE_NORMAL;
 	unsigned int 	m_current_tool  = 0;
@@ -214,7 +240,18 @@ private:
 	// How much to wipe the 1st extruder over the wipe tower at the 1st layer
 	// after the wipe tower brim has been extruded?
 	float  			m_initial_extra_wipe = 0.f;
+	float			m_last_infill_tan = 1000.f;	// impossibly high value
+	bool 			m_plan_brim_finished = false;
 
+		float
+		extrusion_flow(float layer_height = -1.f)
+	{
+		if ( layer_height < 0 )
+			return m_extrusion_flow;
+		return layer_height * ( Width_To_Nozzle_Ratio * Nozzle_Diameter - layer_height * (1-M_PI/4.f)) / (Filament_Area);
+	}
+
+	
 	struct box_coordinates
 	{
 		box_coordinates(float left, float bottom, float width, float height) :
@@ -246,8 +283,34 @@ private:
 		xy rd;	// right lower
 	};
 
+
+	// to store information about tool changes for a given layer
+	struct WipeTowerInfo{
+		struct ToolChange {
+			unsigned int old_tool;
+			unsigned int new_tool;
+			float required_depth;
+			ToolChange(unsigned int old,unsigned int newtool,float depth) : old_tool{old}, new_tool{newtool}, required_depth{depth} {}
+		};
+		float z;		// z position of the layer
+		float height;	// layer height
+		float depth;	// depth of the layer based on all layers above
+		float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
+
+		std::vector<ToolChange> tool_changes;
+
+		WipeTowerInfo(float z_par, float layer_height_par)
+			: z{z_par}, height{layer_height_par} { }
+	};
+
+	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
+	std::vector<WipeTowerInfo> m_plan;
+
+	WipeTowerInfo* m_layer_info;
+
+
 	// Returns gcode for wipe tower brim
-	// sideOnly			-- set to false -- experimental, draw brim on sides of wipe tower 
+	// sideOnly			-- set to false -- experimental, draw brim on sides of wipe tower
 	// offset			-- set to 0		-- experimental, offset to replace brim in front / rear of wipe tower
 	ToolChangeResult toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
 
@@ -269,9 +332,7 @@ private:
 	void toolchange_Wipe(
 		PrusaMultiMaterial::Writer &writer,
 		const box_coordinates  &cleaning_box,
-		bool skip_initial_y_move);
-	
-	void toolchange_Perimeter();
+		bool skip_initial_y_move);	
 };
 
 }; // namespace Slic3r
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 3bd07d3eb..cfc26eeac 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1005,9 +1005,33 @@ 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, WipeTower::PURPOSE_EXTRUDE));
 
+
+    // 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 = initial_extruder_id;
+        for (const 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);
+            for (const auto extruder_id : layer_tools.extruders) {
+                if ((first_layer && extruder_id == initial_extruder_id) || extruder_id != current_extruder_id) {          
+                    wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id);
+                    current_extruder_id = extruder_id;
+                }
+            }
+            if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
+                break;
+        }
+    }
+
+    
+
     // Generate the wipe tower layers.
     m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
-    unsigned int current_extruder_id = initial_extruder_id;
+    wipe_tower.generate(m_wipe_tower_tool_changes);
+    
+    /*unsigned int current_extruder_id = initial_extruder_id;
     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.
@@ -1047,7 +1071,7 @@ void Print::_make_wipe_tower()
         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;

From c6718c94bf4cca861fd22fdf854e2103a5779324 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 21 Dec 2017 14:24:47 +0100
Subject: [PATCH 009/118] Merge fix

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

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 3cb2e481b..8b7f9805c 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1044,14 +1044,15 @@ void Print::_make_wipe_tower()
 
     // 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 = initial_extruder_id;
+        unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
         for (const 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);
             for (const auto extruder_id : layer_tools.extruders) {
-                if ((first_layer && extruder_id == initial_extruder_id) || extruder_id != current_extruder_id) {          
+                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);
                     current_extruder_id = extruder_id;
                 }
@@ -1069,7 +1070,7 @@ void Print::_make_wipe_tower()
     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();
+    /*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)

From 180967484e15d529a132a59f2ac760354cf87298 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 22 Dec 2017 11:26:43 +0100
Subject: [PATCH 010/118] Correction of wipe tower brim generation

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 42 +++++++++++++++------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 14 ++-----
 xs/src/libslic3r/Print.cpp                  |  7 ++--
 3 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 0b3a023a4..e51b43430 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -92,7 +92,7 @@ public:
 		if (! m_preview_suppressed && e > 0.f && len > 0.) {
 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
 			// This is left zero if it is a travel move.
-			float width = float(double(e) * m_filament_area / (len * m_layer_height));
+			float width = float(double(e) * Filament_Area / (len * m_layer_height));
 			// Correct for the roundings of a squished extrusion.
 			width += m_layer_height * float(1. - M_PI / 4.);
 			if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
@@ -258,6 +258,25 @@ public:
 		return *this;
 	};
 
+
+	Writer& set_fan(unsigned int speed)
+	{
+		if (speed == m_last_fan_speed)
+			return *this;
+				
+		if (speed == 0)
+			m_gcode += "M107\n";
+		else
+		{
+			m_gcode += "M106 S";
+			char buf[128];
+			sprintf(buf,"%u\n",(unsigned int)(255.0 * speed / 100.0));
+			m_gcode += buf;
+		}
+		m_last_fan_speed = speed;
+		return *this;
+	}
+
 	Writer& comment_material(WipeTowerPrusaMM::material_type material)
 	{
 		m_gcode += "; material : ";
@@ -294,13 +313,15 @@ private:
 	std::string   m_gcode;
 	std::vector<WipeTower::Extrusion> m_extrusions;
 	float         m_elapsed_time;
-	const double  m_filament_area = 0.25*M_PI*1.75*1.75;
 	float   	  m_angle_deg = 0;
 	WipeTower::xy m_wipe_tower_pos;
 	float 		  m_wipe_tower_width;
 	float		  m_wipe_tower_depth;
+	float		  m_last_fan_speed = 0.f;
 
-	std::string   set_format_X(float x) {
+		std::string
+		set_format_X(float x)
+	{
 		char buf[64];
 		sprintf(buf, " X%.3f", x);
 		m_current_pos.x = x;
@@ -1116,7 +1137,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 
 // 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)
+void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool,bool brim)
 {
 	assert(m_plan.back().z <= z_par);	// refuse to add a layer below the last one
 	
@@ -1131,20 +1152,18 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 														  { 60, 40, 60,  0}};
 
 	float depth = (wipe_volumes[old_tool][new_tool]) / (m_extrusion_flow * Filament_Area); // length of extrusion
+	depth += 6 * 59; // reserved for ramming
+	depth += 2 * 59; // reserved for loading
+	depth -= 2 * 59; // we will also print borders
 	depth = floor(depth / m_wipe_tower_width + 1);	// number of lines to extrude
-	depth += 6; // reserved for ramming
-	depth += 2; // reserved for loading
-	depth -= 2; // we will also print borders
 	depth *= m_perimeter_width; // conversion to distance
 
 	if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan along with the first toolchange
 		m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
 
-	if ( old_tool != new_tool )	{
-		if (!m_plan_brim_finished) {	// this toolchange prints brim, we need it in m_plan, but not to count its depth
-			m_plan_brim_finished = true;
+	if ( brim || old_tool != new_tool )	{
+		if (brim) // this toolchange prints brim, we need it in m_plan, but not to count its depth
 			depth = 0.f;
-		}
 		m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth));
 	}
 
@@ -1173,7 +1192,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 	
 	for (auto layer : m_plan)
 	{
-
 		set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
 
 		for (const auto &toolchange : layer.tool_changes)
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 574accd75..04e0b004a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -92,7 +92,7 @@ public:
 
 	// Setter for 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);
+	void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim);
 
 	void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
 
@@ -131,6 +131,7 @@ public:
 		// Calculates extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height
 		m_extrusion_flow = extrusion_flow(layer_height);
 
+		// FIXME - ideally get rid of set_layer altogether and iterate through m_plan in generate(...)
 		m_layer_info = nullptr;
 		for (auto &a : m_plan)
 			if ( a.z > print_z - WT_EPSILON && a.z < print_z + WT_EPSILON ) {
@@ -170,12 +171,7 @@ public:
 	// the wipe tower has been completely covered by the tool change extrusions,
 	// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
 	virtual bool 			 layer_finished() const {
-		
-		if (m_is_first_layer) {
-			return (m_wipe_tower_depth - WT_EPSILON < m_current_wipe_start_y);
-		}
-		else
-			return (m_layer_info->depth - WT_EPSILON < m_current_wipe_start_y);
+		return ( (m_is_first_layer ? m_wipe_tower_depth : m_layer_info->depth) - WT_EPSILON < m_current_wipe_start_y);
 	}
 
 
@@ -241,10 +237,8 @@ private:
 	// after the wipe tower brim has been extruded?
 	float  			m_initial_extra_wipe = 0.f;
 	float			m_last_infill_tan = 1000.f;	// impossibly high value
-	bool 			m_plan_brim_finished = false;
 
-		float
-		extrusion_flow(float layer_height = -1.f)
+	float extrusion_flow(float layer_height = -1.f) const
 	{
 		if ( layer_height < 0 )
 			return m_extrusion_flow;
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 8b7f9805c..64f4d6046 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1050,10 +1050,10 @@ void Print::_make_wipe_tower()
         for (const 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);
+            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);
+                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());
                     current_extruder_id = extruder_id;
                 }
             }
@@ -1066,7 +1066,6 @@ 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.

From 37bbeeb9d0db258dd0d4a7cf95dd723579e12a3f Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 4 Jan 2018 12:03:06 +0100
Subject: [PATCH 011/118] Parametrization of ramming and loading sequence -
 first steps

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 190 +++++++++++++++-----
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |   1 +
 2 files changed, 142 insertions(+), 49 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index e51b43430..ce9b8883a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -15,6 +15,14 @@
 #endif
 
 
+// experimental: ramming speed (mm^3/s) sampled in 0.25s intervals (one filament so far)
+const std::vector<float> ramming_speed = {7.6, 7.6, 7.6, 7.6, 9.0, 9.0, 9.0, 10.7, 10.7, 10.7};
+// experimental: time requested for cooling in seconds (common for all materials so far)
+const float cooling_time = 14; // PVA: 20; SCAFF: 17
+const float loading_volume = 20;
+
+
+
 namespace Slic3r
 {
 
@@ -562,6 +570,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		}
 	}*/
 
+	// Finds this toolchange info
 	if (tool != (unsigned int)(-1)) {
 		for (const auto &b : m_layer_info->tool_changes)
 			if ( b.new_tool == tool )
@@ -779,15 +788,51 @@ void WipeTowerPrusaMM::toolchange_Unload(
 {
 	float xl = cleaning_box.ld.x + 0.5f * m_perimeter_width;
 	float xr = cleaning_box.rd.x - 0.5f * m_perimeter_width;
-	float y_step = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width;
-
+	
 	writer.append("; CP TOOLCHANGE UNLOAD\n");
-
+	
 	// Ram the hot material out of the extruder melt zone.
 	// Current extruder position is on the left, one perimeter inside the cleaning box in both X and Y.
-	float e0 = m_perimeter_width * m_extrusion_flow;
-	float e = (xr - xl) * m_extrusion_flow;
-	switch (current_material)
+	//float e0 = m_perimeter_width * m_extrusion_flow;
+	//float e = (xr - xl) * m_extrusion_flow;
+	//float y_step = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * ramming_step_multiplicator;
+
+	constexpr float ramming_step_multiplicator = 1.f;				// extra spacing needed for some materials
+	const float line_width = m_line_width * 1.5f;				  	// desired ramming line thickness
+
+	float y_step = (m_current_shape == SHAPE_NORMAL ? 1.f : -1.f) * // spacing between lines in mm
+				   line_width * ramming_step_multiplicator;
+	
+	
+	int i = 0;											// iterates through ramming_speed
+	m_left_to_right = true;								// current direction of ramming
+	float remaining = xr - xl - 2 * m_perimeter_width;  // keeps track of distance to the next turnaround
+	float e_done = 0;									// measures E move done from each segment
+
+	writer.travel(xl + m_perimeter_width, writer.y() + y_step * 0.2); // move to starting position
+
+	while (i < ramming_speed.size()) {
+		const float x = (ramming_speed[i] * 0.25f) / 				// extrusion length to get desired line_width
+						(m_layer_height * (line_width - m_layer_height * (1. - M_PI / 4.)));
+		const float e = ramming_speed[i] * 0.25f / Filament_Area;	// transform volume per sec to E move;
+		const float dist = std::min(x - e_done, remaining);			// distance to travel for the next 0.25s
+		writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (0.25 / 60.));
+		remaining -= dist;
+
+		if ( remaining < WT_EPSILON ) {						// we reached a turning point
+			writer.travel(writer.x(), writer.y() + y_step);
+			m_left_to_right = !m_left_to_right;
+			remaining = xr - xl - 2 * m_perimeter_width;
+		}
+		e_done += dist; 				// subtract what was actually transversed		
+		if (e_done > x - WT_EPSILON) {	// current segment finished
+			++i;
+			e_done = 0;
+		}
+	}
+
+
+	/*switch (current_material)
 	{
 	case ABS:
    		// ramming          start                    end                  y increment     amount feedrate
@@ -803,7 +848,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.5f, 0,  1.75f * e, 4800)
 			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.5f, 0,  1.75f * e, 5000);
 		break;
-	case SCAFF:
+	case PET: //!!! SCAFF (PET only used for testing):
 		writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width,     y_step * 2.f,  0,  1.75f * e, 4000)
 			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 3.f,  0,  2.34f * e, 4600)
 			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 3.f,  0,  2.63f * e, 5200);
@@ -814,7 +859,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 		writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width,     y_step * 0.2f, 0,  1.60f * e, 4000)
 			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.2f, e0, 1.65f * e, 4600)
 			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, e0, 1.74f * e, 5200);
-	}
+	}*/
 
 	// Pull the filament end into a cooling tube.
 	writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
@@ -827,9 +872,45 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	if (std::abs(writer.x() - xl) < std::abs(writer.x() - xr))
 		std::swap(xl, xr);
 	// Horizontal cooling moves will be performed at the following Y coordinate:
-	writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
+	/*writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
+		  .suppress_preview();*/
+
+	/*constexpr float min_cool_speed = 1600;
+	constexpr float max_cool_speed = 2400;
+
+	const int num_of_intervals = 4;
+
+	writer.travel(writer.x(), writer.y() + y_step)
+		  .retract(-3,min_cool_speed)
 		  .suppress_preview();
-	switch (current_material)
+
+	int filament_direction = -1;			// retract first
+	float cool_speed = min_cool_speed;
+	// Numbers 12 (24) below fix velocity in E to 12 times less than in X (total move in X assumed to be >> move in E)
+	
+	for (int counter=0 ; counter < num_of_intervals ; ++counter) {
+		float needed_e_move = cool_speed * (cooling_time / num_of_intervals) / 12.f; // distance yet to move in E axis
+		do {			
+			remaining = ( left_to_right ? xr-m_perimeter_width-writer.x() : -xl-m_perimeter_width+writer.x()); // distance to the turning point
+			float xmove = std::min( std::fabs(needed_e_move) * 12.f, remaining * 2.f);						  // distance to travel
+			printf("posun z %f na %f",writer.x(),writer.x()+xmove / 2.f * (left_to_right ? 1 : -1));
+				writer.cool(writer.x() + xmove / 2.f * (left_to_right ? 1 : -1), writer.x(), filament_direction * xmove / 24.f, filament_direction * xmove / 24.f, cool_speed);
+			printf(", E %f, rychlost %f\n\n",filament_direction * xmove /12.f,cool_speed);
+			needed_e_move -= xmove/12.f;
+			if (needed_e_move > WT_EPSILON)
+				filament_direction *= -1;
+			else
+				left_to_right = !left_to_right;
+
+		} while (needed_e_move > WT_EPSILON);
+		if (filament_direction==-1)
+			cool_speed += (max_cool_speed - min_cool_speed) / num_of_intervals;
+	}
+
+	writer.retract(3, max_cool_speed);*/
+
+	
+	/*switch (current_material)
 	{
 	case PVA:
 		writer.cool(xl, xr, 3, -5, 1600)
@@ -851,7 +932,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 			  .cool(xl, xr, 5, -5, 2000)
 			  .cool(xl, xr, 5, -5, 2400)
 			  .cool(xl, xr, 5, -3, 2400);
-	}
+	}*/
 
 	writer.resume_preview()
 		  .flush_planner_queue();
@@ -881,13 +962,13 @@ void WipeTowerPrusaMM::toolchange_Load(
 	PrusaMultiMaterial::Writer &writer,
 	const box_coordinates  &cleaning_box)
 {
-	float xl = cleaning_box.ld.x + m_perimeter_width;
-	float xr = cleaning_box.rd.x - m_perimeter_width;
+	float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f;
+	float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f;
 	//FIXME flipping left / right side, so that the following toolchange_Wipe will start
 	// where toolchange_Load ends. 
-	std::swap(xl, xr);
+	//std::swap(xl, xr);
 
-	writer.append("; CP TOOLCHANGE LOAD\n")
+	/*writer.append("; CP TOOLCHANGE LOAD\n")
 	// Load the filament while moving left / right,
 	// so the excess material will not create a blob at a single position.
 		  .suppress_preview()
@@ -899,9 +980,9 @@ void WipeTowerPrusaMM::toolchange_Load(
 		  .load_move_x(xr, 20, 1600)
 		  .load_move_x(xl, 10, 1000)
 		  .resume_preview();
-
+*/
 	// Extrude first five lines (just three lines if colorInit is set).
-	writer.extrude(xr, writer.y(), 1600);
+	/*writer.extrude(xr, writer.y(), 1600);
 	bool   colorInit = false;
 	size_t pass = colorInit ? 1 : 2;
 	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
@@ -910,12 +991,31 @@ void WipeTowerPrusaMM::toolchange_Load(
 		writer.extrude(xl, writer.y(), 		2200);
 		writer.travel (xl, writer.y() + dy, 7200);
 	 	writer.extrude(xr, writer.y(), 		2200);
+	}*/
+
+	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
+	float x = (loading_volume) / (Filament_Area * m_extrusion_flow); // extrusion length to extrude desired volume
+	while (x > WT_EPSILON) {		
+		float remaining = (m_left_to_right ? xr - writer.x() : writer.x() - xl );
+		float dist = std::min(x, remaining);
+		writer.extrude(writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, writer.y(), 2200);
+		x -= dist;
+		
+		if (x > WT_EPSILON) {	// don't switch it for the last (unfinished) line
+			m_left_to_right = !m_left_to_right;
+			writer.travel(writer.x(), writer.y() + dy, 7200);
+		}
+		else	// advance just a fraction for debugging (to see where the load ends) (FIXME - HAS to be deleted, can pass the edge)
+			writer.travel(writer.x()+(m_left_to_right ? 0.1f : -0.1f),writer.y());
 	}
 
 	// Reset the extruder current to the normal value.
 	writer.set_extruder_trimpot(550);
 }
 
+
+
+
 // Wipe the newly loaded filament until the end of the assigned wipe area.
 void WipeTowerPrusaMM::toolchange_Wipe(
 	PrusaMultiMaterial::Writer &writer,
@@ -926,8 +1026,8 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f))
 		  .append("; CP TOOLCHANGE WIPE\n");
 	float wipe_coeff = m_is_first_layer ? 0.5f : 1.f;
-	float xl = cleaning_box.ld.x + 2.f * m_perimeter_width;
-	float xr = cleaning_box.rd.x - 2.f * m_perimeter_width;
+	const float& xl = cleaning_box.ld.x; // + 2.f * m_perimeter_width;
+	const float& xr = cleaning_box.rd.x; // - 2.f * m_perimeter_width;
 	// Wipe speed will increase up to 4800.
 	float wipe_speed 	 = 4200.f;
 	float wipe_speed_inc = 50.f;
@@ -935,27 +1035,22 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	// Y increment per wipe line.
 	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
 
-	for (bool p = true; 
-		// Next wipe line fits the cleaning box (and not forgetting about border)
-		((m_current_shape == SHAPE_NORMAL) ?
-			(writer.y() <= cleaning_box.lu.y - 1.5f * m_perimeter_width) :
-			(writer.y() >= cleaning_box.ld.y + 1.5f * m_perimeter_width));
-		p = ! p)
+	for ( int i = 0 ; true ; ++i )
 	{
 		wipe_speed = std::min(wipe_speed_max, wipe_speed + wipe_speed_inc);
-		if (skip_initial_y_move)
-			skip_initial_y_move = false;
+		if (m_left_to_right)
+			writer.extrude(xr - (i % 4 == 0 ? 0 : m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
 		else
-			writer.extrude(xl - (p ? m_perimeter_width / 2 : m_perimeter_width), writer.y() + dy, wipe_speed * wipe_coeff);
-		writer.extrude(xr + (p ? m_perimeter_width : m_perimeter_width * 2), writer.y(), wipe_speed * wipe_coeff);
-		// Next wipe line fits the cleaning box.
-		if ((m_current_shape == SHAPE_NORMAL) ?
-			(writer.y() > cleaning_box.lu.y - m_perimeter_width) :
-			(writer.y() < cleaning_box.ld.y + m_perimeter_width))
+			writer.extrude(xl + (i % 4 == 1 ? 0 : m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
+		
+		if ((m_current_shape == SHAPE_NORMAL) ?		// in case next line would not fit
+			(writer.y() > cleaning_box.lu.y - m_perimeter_width * 1.5f) :
+			(writer.y() < cleaning_box.ld.y + m_perimeter_width * 1.5f))
 			break;
-		wipe_speed = std::min(wipe_speed_max, wipe_speed + wipe_speed_inc);
-		writer.extrude(xr + m_perimeter_width, writer.y() + dy, wipe_speed * wipe_coeff);
-		writer.extrude(xl - m_perimeter_width, writer.y());
+
+		// stepping to the next line:
+		writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * m_perimeter_width, writer.y() + dy);
+		m_left_to_right = !m_left_to_right;
 	}
 	// Reset the extrusion flow.
 	writer.set_extrusion_flow(m_extrusion_flow);
@@ -1081,10 +1176,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 				const size_t zig_zags_num = (fill_box.rd.x - fill_box.ld.x - m_perimeter_width * 12.f) / max_bridge_distance;
 				const float step = (fill_box.rd.x - fill_box.ld.x - m_perimeter_width * 12.f) / (float)zig_zags_num;
 
-				float offsety = std::max(0.f,  dy - m_last_infill_tan * (step - m_perimeter_width));
+				float offsetx = std::max(0.f, dy / m_last_infill_tan > step / 2.f ? step - dy / m_last_infill_tan : 0.f);
+				float offsety = ( offsetx != 0 ? 0 : std::max(0.f,  dy - m_last_infill_tan * (step - m_perimeter_width)) );
 				if (offsety < m_last_infill_tan * m_perimeter_width + WT_EPSILON || offsety > dy / 2.f)
 					offsety = 0.f;
-				float offsetx = ( offsety != 0 ? 0 : std::max(0.f, dy / m_last_infill_tan > step / 2.f ? step - dy / m_last_infill_tan : 0.f ) );
+				
 				
 				for (size_t i = 0; i < zig_zags_num; ++i)
 				{
@@ -1140,23 +1236,19 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool,bool brim)
 {
 	assert(m_plan.back().z <= z_par);	// refuse to add a layer below the last one
-	
-	// volumes in mm^3 required for ramming
-	// hardcoded so far, in future should be calculated by integrating the speed/time curve
-	const std::vector<float> ram_volumes = {22,22,22,22};
-	
+		
 	// volumes in mm^3 required for wipe: {{from 0 to ...},{from 1 to ...},{from 2 to ...},{from 3 to ...}}, usage [from][to]
-	const std::vector<std::vector<float>> wipe_volumes = {{  0, 40, 60, 80},
-														  {100,  0,120,100},
-														  { 80,107,  0,110},
-														  { 60, 40, 60,  0}};
+	const std::vector<std::vector<float>> wipe_volumes = {{  0, 40, 60,100},
+														  {100,  0,130,100},
+														  { 80, 90,  0,110},
+														  { 50, 40, 60,  0}};
 
-	float depth = (wipe_volumes[old_tool][new_tool]) / (m_extrusion_flow * Filament_Area); // length of extrusion
+	float depth = (wipe_volumes[old_tool][new_tool]) / (extrusion_flow(layer_height_par) * Filament_Area); // length of extrusion
 	depth += 6 * 59; // reserved for ramming
 	depth += 2 * 59; // reserved for loading
 	depth -= 2 * 59; // we will also print borders
 	depth = floor(depth / m_wipe_tower_width + 1);	// number of lines to extrude
-	depth *= m_perimeter_width; // conversion to distance
+	depth *= m_line_width; // conversion to distance
 
 	if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan along with the first toolchange
 		m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 04e0b004a..6ca807528 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -237,6 +237,7 @@ private:
 	// after the wipe tower brim has been extruded?
 	float  			m_initial_extra_wipe = 0.f;
 	float			m_last_infill_tan = 1000.f;	// impossibly high value
+	bool 			m_left_to_right = true;
 
 	float extrusion_flow(float layer_height = -1.f) const
 	{

From 5ca0a2f37dc69aed796d84db6a82f99bdbc50d8c Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 21 Feb 2018 13:07:32 +0100
Subject: [PATCH 012/118] Parametrized toolchanges, experiments with sparse
 wipe tower, etc

---
 xs/src/libslic3r/GCode/WipeTower.hpp        |   1 +
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 792 ++++++++++----------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 105 ++-
 3 files changed, 441 insertions(+), 457 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index 28549ae64..289e8e122 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -17,6 +17,7 @@ public:
 	struct xy
 	{
 		xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
+		xy(xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
 		xy  operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
 		xy  operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
 		xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index ce9b8883a..c409b5f20 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -1,3 +1,18 @@
+/*
+
+TODO LIST
+---------
+
+1. cooling moves - DONE
+2. priming extrusions - DONE (fixed wiping volume so far)
+3. account for perimeter and finish_layer extrusions and subtract it from last wipe
+4. Peter's wipe tower - layer's are not exactly square
+5. Peter's wipe tower - variable width for higher levels
+6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
+7. Peter's wipe tower - enable enhanced first layer adhesion 
+
+*/
+
 #include "WipeTowerPrusaMM.hpp"
 
 #include <assert.h>
@@ -14,14 +29,35 @@
 #define strcasecmp _stricmp
 #endif
 
-
+constexpr bool  peters_wipe_tower = false;	// sparse wipe tower inspired by Peter's post processor - not finished yet
+constexpr float min_layer_difference = 2*m_perimeter_width;
+constexpr float max_bridge_distance = 10.f; // in mm
+constexpr bool  improve_first_layer_adhesion = true;
 // experimental: ramming speed (mm^3/s) sampled in 0.25s intervals (one filament so far)
 const std::vector<float> ramming_speed = {7.6, 7.6, 7.6, 7.6, 9.0, 9.0, 9.0, 10.7, 10.7, 10.7};
+constexpr float ramming_step_multiplicator = 1.2f; // extra spacing may be needed for some materials
+constexpr float ramming_line_width_multiplicator = 1.5f;
+
 // experimental: time requested for cooling in seconds (common for all materials so far)
-const float cooling_time = 14; // PVA: 20; SCAFF: 17
-const float loading_volume = 20;
+constexpr float cooling_time = 14; // PVA: 20; SCAFF: 17; PLA+others: 14
 
 
+// volumes in mm^3 required for wipe: {{from 0 to ...},{from 1 to ...},{from 2 to ...},{from 3 to ...}}, usage [from][to]
+const std::vector<std::vector<float>> wipe_volumes = {{  0,120, 10, 50},
+													  { 20,  0, 30, 40},
+													  { 90, 20,  0, 85},
+													  {100,140, 30,  0}};
+
+/*const std::vector<std::vector<float>> wipe_volumes = {{0, 67, 67, 67},
+													  {67, 0, 67, 67},
+													  {67, 67, 0, 67},
+													  {67, 67, 67, 0}};
+*/
+/*const std::vector<std::vector<float>> wipe_volumes = {{0, 10, 10, 10},
+													  {10, 0, 10, 10},
+													  {10, 10, 0, 10},
+													  {10, 10, 10, 0}};
+*/
 
 namespace Slic3r
 {
@@ -35,8 +71,8 @@ public:
 		m_current_pos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
 		m_current_z(0.f),
 		m_current_feedrate(0.f),
-		m_extrusion_flow(0.f),
 		m_layer_height(0.f),
+		m_extrusion_flow(0.f),
 		m_preview_suppressed(false),
 		m_elapsed_time(0.f) {}
 
@@ -60,6 +96,9 @@ public:
 	Writer&				 set_rotation(WipeTower::xy& pos, float width, float depth, float angle)
 		{ m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
 
+	Writer&				 set_y_shift(float shift)
+		{ m_y_shift = shift; return (*this); }
+
 	// 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.
@@ -79,7 +118,7 @@ public:
 	float                y()     const { return m_current_pos.y; }
 	const WipeTower::xy& pos()   const { return m_current_pos; }
 	const WipeTower::xy	 start_pos_rotated() const { return m_start_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
-	const WipeTower::xy pos_rotated() const { return m_current_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
+	const WipeTower::xy  pos_rotated() const { return m_current_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
 
 	// Extrude with an explicitely provided amount of extrusion.
@@ -92,10 +131,11 @@ public:
 		float dx = x - m_current_pos.x;
 		float dy = y - m_current_pos.y;
 		double len = sqrt(dx*dx+dy*dy);
-		
+
+
 		// For rotated wipe tower, transform position to printer coordinates
-		WipeTower::xy rotated_current_pos(m_current_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
-		WipeTower::xy rot(WipeTower::xy(x,y).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
+		WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
+		WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
 
 		if (! m_preview_suppressed && e > 0.f && len > 0.) {
 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
@@ -321,7 +361,8 @@ private:
 	std::string   m_gcode;
 	std::vector<WipeTower::Extrusion> m_extrusions;
 	float         m_elapsed_time;
-	float   	  m_angle_deg = 0;
+	float   	  m_angle_deg = 0.f;
+	float		  m_y_shift = 0.f;
 	WipeTower::xy m_wipe_tower_pos;
 	float 		  m_wipe_tower_width;
 	float		  m_wipe_tower_depth;
@@ -427,7 +468,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 {
 	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
 
-	float wipe_area = m_wipe_area;
 	// Calculate the amount of wipe over the wipe tower brim following the prime, decrease wipe_area
 	// with the amount of material extruded over the brim.
 	// DOESN'T MAKE SENSE NOW, wipe tower dimensions are still unknown at this point
@@ -454,7 +494,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 //	box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
 	//FIXME: set the width properly
 	constexpr float prime_section_width = 60.f;
-	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, wipe_area);
+	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 15.f);
 
 	PrusaMultiMaterial::Writer writer;
 	writer.set_extrusion_flow(m_extrusion_flow)
@@ -466,37 +506,26 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 		  .append(";--------------------\n")
 		  .speed_override(100);
 
-	// Always move to the starting position.
-	writer.set_initial_position(xy(0.f, 0.f))
-		  .travel(cleaning_box.ld, 7200)
-	// Increase the extruder driver current to allow fast ramming.
-		  .set_extruder_trimpot(750);
+	writer.set_initial_position(xy(0.f, 0.f))	// Always move to the starting position
+		.travel(cleaning_box.ld, 7200)
+		.set_extruder_trimpot(750); 			// Increase the extruder driver current to allow fast ramming.
 
 	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		float y_end = 0.f;
 		for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
 			unsigned int tool = tools[idx_tool];
-			// Select the tool, set a speed override for soluble and flex materials.
-			toolchange_Change(writer, tool, m_material[tool]);
-			// Prime the tool.
-			toolchange_Load(writer, cleaning_box);
+			m_left_to_right = true;
+			toolchange_Change(writer, tool, m_material[tool]); // Select the tool, set a speed override for soluble and flex materials.
+			toolchange_Load(writer, cleaning_box); // Prime the tool.
 			if (idx_tool + 1 == tools.size()) {
 				// Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
-				if (last_wipe_inside_wipe_tower) {
-					// Shrink the last wipe area to the area of the other purge areas,
-					// remember the last initial wipe width to be purged into the 1st layer of the wipe tower.
-					this->m_initial_extra_wipe = std::max(0.f, wipe_area - (y_end + 0.5f * m_line_width - cleaning_box.ld.y));
-					cleaning_box.lu.y -= this->m_initial_extra_wipe;
-					cleaning_box.ru.y -= this->m_initial_extra_wipe;
-				}
-				toolchange_Wipe(writer, cleaning_box, false);
+				toolchange_Wipe(writer, cleaning_box, 20.f);
 			} else {
 				// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
-				writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
-				// Change the extruder temperature to the temperature of the next filament before starting the cooling moves.
-				toolchange_Unload(writer, cleaning_box, m_material[m_current_tool], m_first_layer_temperature[tools[idx_tool+1]]);
-				// Save the y end of the non-last priming area.
-				y_end = writer.y();
+				//writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
+				toolchange_Wipe(writer, cleaning_box , 5.f);
+				box_coordinates box = cleaning_box;
+				box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width);
+				toolchange_Unload(writer, box , m_material[m_current_tool], m_first_layer_temperature[tools[idx_tool + 1]]);
 				cleaning_box.translate(prime_section_width, 0.f);
 				writer.travel(cleaning_box.ld, 7200);
 			}
@@ -541,7 +570,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	}
 	
 	float wipe_area = 0.f;
-	/*if (++ m_idx_tool_change_in_layer < (unsigned int)m_max_color_changes && last_in_layer) {
+	{/*if (++ m_idx_tool_change_in_layer < (unsigned int)m_max_color_changes && last_in_layer) {
 		// This tool_change() call will be followed by a finish_layer() call.
 		// Try to shrink the wipe_area to save material, as less than usual wipe is required
 		// if this step is foolowed by finish_layer() extrusions wiping the same extruder.
@@ -568,29 +597,39 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 				break;
 			}
 		}
-	}*/
+	}*/}
 
+	bool last_change_in_layer = false;
+	float wipe_volume = 0.f;
+	
 	// Finds this toolchange info
-	if (tool != (unsigned int)(-1)) {
+	if (tool != (unsigned int)(-1))
+	{
 		for (const auto &b : m_layer_info->tool_changes)
-			if ( b.new_tool == tool )
-				wipe_area = b.required_depth;
+			if ( b.new_tool == tool ) {
+				wipe_volume = wipe_volumes[b.old_tool][b.new_tool];
+				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;
+				break;
+			}
 	}
 	else {
 		// Otherwise we are going to Unload only. And m_layer_info would be invalid.
 	}
-	
+
 	box_coordinates cleaning_box(
-		m_wipe_tower_pos + xy(0.f, m_current_wipe_start_y + 0.5f * m_perimeter_width),
-		m_wipe_tower_width,
-		wipe_area - m_perimeter_width);
+		m_wipe_tower_pos + xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
+		m_wipe_tower_width - m_perimeter_width,
+		(tool != (unsigned int)(-1) ? m_layer_info->depth : m_wipe_tower_depth-m_perimeter_width));
 
 	PrusaMultiMaterial::Writer writer;
 	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_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
+		.set_y_shift(m_y_shift)
 		.append(";--------------------\n"
 				"; CP TOOLCHANGE START\n")
 		.comment_with_value(" toolchange #", m_num_tool_changes)
@@ -598,8 +637,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.append(";--------------------\n")
 		.speed_override(100);
 
-	xy initial_position = ((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) + 
-    	xy(m_perimeter_width, ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width);
+	xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
 
 	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
 		// Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower.
@@ -623,35 +661,29 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
 		// Increase the extruder driver current to allow fast ramming.
 		writer.set_extruder_trimpot(750);
+		
 		// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
-
-		if (tool != (unsigned int)-1) {
+		if (tool != (unsigned int)-1){ 			// This is not the last change.
 			toolchange_Unload(writer, cleaning_box, m_material[m_current_tool],
-				m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
-			// This is not the last change.
-			// Change the tool, set a speed override for soluble and flex materials.
-			toolchange_Change(writer, tool, m_material[tool]);
-			toolchange_Load(writer, cleaning_box);
-			// Wipe the newly loaded filament until the end of the assigned wipe area.
-			toolchange_Wipe(writer, cleaning_box, false);
-			// Draw a perimeter around cleaning_box and wipe.
-			box_coordinates box = cleaning_box;
-			if (m_current_shape == SHAPE_REVERSED) {
-				std::swap(box.lu, box.ld);
-				std::swap(box.ru, box.rd);
-			}
-			// Draw a perimeter around cleaning_box.
-			writer.travel(box.lu, 7000)
-				  .extrude(box.ld, 3200).extrude(box.rd)
-				  .extrude(box.ru).extrude(box.lu);
-			// Wipe the nozzle.
-			//if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
+							  m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);			
+			toolchange_Change(writer, tool, m_material[tool]); // Change the tool, set a speed override for soluble and flex materials.
+			toolchange_Load(writer, cleaning_box);			
+			toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
+
 			// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
-				writer.travel(box.ru, 7200)
-			  		  .travel(box.lu);
+			/*box_coordinates box = cleaning_box;
+			writer.travel(box.ru, 7200)
+					.travel(box.lu);*/
 		} else
 			toolchange_Unload(writer, cleaning_box, m_material[m_current_tool], m_temperature[m_current_tool]);
 
+		if (last_change_in_layer) // draw perimeter line
+			writer.travel(m_wipe_tower_pos, 7000)
+				.extrude(m_wipe_tower_pos + xy(0, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width), 3200)
+				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width))
+				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, 0))
+				.extrude(m_wipe_tower_pos);
+
 		// Reset the extruder current to a normal value.
 		writer.set_extruder_trimpot(550)
 			  .feedrate(6000)
@@ -662,7 +694,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 					  "\n\n");
 
 	    ++ m_num_tool_changes;
-	    m_current_wipe_start_y += wipe_area;
+	    //m_current_wipe_start_y += wipe_area;
+		m_depth_traversed += wipe_area;
 	}
 
 	ToolChangeResult result;
@@ -681,18 +714,16 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 	const box_coordinates wipeTower_box(
 		m_wipe_tower_pos,
 		m_wipe_tower_width,
-		m_wipe_tower_depth - m_perimeter_width / 2);
+		m_wipe_tower_depth);
 
 	PrusaMultiMaterial::Writer writer;
 	writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
-		// Let the writer know the current Z position as a base for Z-hop.
-		.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)
-		.append(
-			";-------------------------------------\n"
-			"; CP WIPE TOWER FIRST LAYER BRIM START\n");
+		  .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"
+				  "; CP WIPE TOWER FIRST LAYER BRIM START\n");
 
 	xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
 
@@ -705,66 +736,29 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 		writer.set_initial_position(initial_position);
 
 	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		// Prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower.
-		writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), 
-			1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
+
+		writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
+								1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
 
 		// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
 		// toolchange_Change(writer, int(tool), m_material[tool]);
+		
+		// Extrude 4 rounds of a brim around the future wipe tower.
+		box_coordinates box(wipeTower_box);			
+		box.expand(m_perimeter_width);
+		for (size_t i = 0; i < 4; ++ i) {
+			writer.travel (box.ld, 7000)
+					.extrude(box.lu, 2100).extrude(box.ru)
+					.extrude(box.rd      ).extrude(box.ld);
+			box.expand(m_perimeter_width);
+		}	
 
-		if (sideOnly) {
-			float x_offset = m_perimeter_width;
-			for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
-				writer.travel (wipeTower_box.ld + xy(- x_offset,   y_offset), 7000)
-					  .extrude(wipeTower_box.lu + xy(- x_offset, - y_offset), 2100);
-			writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000);
-			x_offset = m_perimeter_width;
-			for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
-				writer.travel (wipeTower_box.rd + xy(x_offset,   y_offset), 7000)
-					  .extrude(wipeTower_box.ru + xy(x_offset, - y_offset), 2100);
-		} else {
-			// Extrude 4 rounds of a brim around the future wipe tower.
-			box_coordinates box(wipeTower_box);
-			//FIXME why is the box shifted in +Y by 0.5f * m_perimeter_width?
-			box.translate(0.f, 0.5f * m_perimeter_width);
-			box.expand(0.5f * m_perimeter_width);
-			for (size_t i = 0; i < 4; ++ i) {
-				writer.travel (box.ld, 7000)
-					  .extrude(box.lu, 2100).extrude(box.ru)
-					  .extrude(box.rd      ).extrude(box.ld);
-				box.expand(m_perimeter_width);
-			}
-		}
-
-		if (m_initial_extra_wipe > m_perimeter_width * 1.9f) {
-			box_coordinates cleaning_box(
-				m_wipe_tower_pos + xy(0.f, 0.5f * m_perimeter_width),
-				m_wipe_tower_width, 
-				m_initial_extra_wipe - m_perimeter_width);
-		    writer.travel(cleaning_box.ld + xy(m_perimeter_width, 0.5f * m_perimeter_width), 6000);
-			// Wipe the newly loaded filament until the end of the assigned wipe area.
-			toolchange_Wipe(writer, cleaning_box, true);
-			// Draw a perimeter around cleaning_box.
-			writer.travel(cleaning_box.lu, 7000)
-				  .extrude(cleaning_box.ld, 3200).extrude(cleaning_box.rd)
-				  .extrude(cleaning_box.ru).extrude(cleaning_box.lu);
-		    m_current_wipe_start_y = m_initial_extra_wipe;
-		}
-
-		// Move to the front left corner.
-		writer.travel(wipeTower_box.ld, 7000);
-
-		//if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
-			// Wipe along the front edge.
-		// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
-			writer.travel(wipeTower_box.rd)
-			      .travel(wipeTower_box.ld);
-			  
-	    writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
+		writer.travel(wipeTower_box.ld, 7000); // Move to the front left corner.
+		writer.travel(wipeTower_box.rd) // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
+			  .travel(wipeTower_box.ld);
+		writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
 				      ";-----------------------------------\n");
-
 		// Mark the brim as extruded.
-		///m_idx_tool_change_in_layer = 0;
 		m_print_brim = false;
 	}
 
@@ -779,6 +773,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 	return result;
 }
 
+
+
 // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
 void WipeTowerPrusaMM::toolchange_Unload(
 	PrusaMultiMaterial::Writer &writer,
@@ -786,53 +782,46 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	const material_type		 current_material,
 	const int 				 new_temperature)
 {
-	float xl = cleaning_box.ld.x + 0.5f * m_perimeter_width;
-	float xr = cleaning_box.rd.x - 0.5f * m_perimeter_width;
+	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");
 	
-	// Ram the hot material out of the extruder melt zone.
-	// Current extruder position is on the left, one perimeter inside the cleaning box in both X and Y.
-	//float e0 = m_perimeter_width * m_extrusion_flow;
-	//float e = (xr - xl) * m_extrusion_flow;
-	//float y_step = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * ramming_step_multiplicator;
+	const float line_width = m_line_width * ramming_line_width_multiplicator; // desired ramming line thickness
+	const float y_step = line_width * ramming_step_multiplicator * m_extra_spacing;			  // spacing between lines in mm
 
-	constexpr float ramming_step_multiplicator = 1.f;				// extra spacing needed for some materials
-	const float line_width = m_line_width * 1.5f;				  	// desired ramming line thickness
-
-	float y_step = (m_current_shape == SHAPE_NORMAL ? 1.f : -1.f) * // spacing between lines in mm
-				   line_width * ramming_step_multiplicator;
-	
-	
-	int i = 0;											// iterates through ramming_speed
+	unsigned i = 0;										// iterates through ramming_speed
 	m_left_to_right = true;								// current direction of ramming
-	float remaining = xr - xl - 2 * m_perimeter_width;  // keeps track of distance to the next turnaround
+	float remaining = xr - xl ;							// keeps track of distance to the next turnaround
 	float e_done = 0;									// measures E move done from each segment
+	
+	writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
 
-	writer.travel(xl + m_perimeter_width, writer.y() + y_step * 0.2); // move to starting position
-
-	while (i < ramming_speed.size()) {
-		const float x = (ramming_speed[i] * 0.25f) / 				// extrusion length to get desired line_width
-						(m_layer_height * (line_width - m_layer_height * (1. - M_PI / 4.)));
-		const float e = ramming_speed[i] * 0.25f / Filament_Area;	// transform volume per sec to E move;
-		const float dist = std::min(x - e_done, remaining);			// distance to travel for the next 0.25s
-		writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (0.25 / 60.));
+	while (i < ramming_speed.size())
+	{
+		const float x = volume_to_length(ramming_speed[i] * 0.25f, line_width, m_layer_height);
+		const float e = ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move;
+		const float dist = std::min(x - e_done, remaining);		  // distance to travel for either the next 0.25s, or to the next turnaround
+		const float actual_time = dist/x * 0.25;
+		writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
 		remaining -= dist;
 
-		if ( remaining < WT_EPSILON ) {						// we reached a turning point
-			writer.travel(writer.x(), writer.y() + y_step);
+		if (remaining < WT_EPSILON)	{ // we reached a turning point
+			writer.travel(writer.x(), writer.y() + y_step, 7200);
 			m_left_to_right = !m_left_to_right;
-			remaining = xr - xl - 2 * m_perimeter_width;
+			remaining = xr - xl;
 		}
-		e_done += dist; 				// subtract what was actually transversed		
-		if (e_done > x - WT_EPSILON) {	// current segment finished
+		e_done += dist; // subtract what was actually done
+		if (e_done > x - WT_EPSILON) { // current segment finished
 			++i;
 			e_done = 0;
 		}
 	}
+	WipeTower::xy end_of_ramming(writer.x(),writer.y());
 
-
-	/*switch (current_material)
+// Alex's old ramming:
+{
+  /*switch (current_material)
 	{
 	case ABS:
    		// ramming          start                    end                  y increment     amount feedrate
@@ -848,7 +837,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.5f, 0,  1.75f * e, 4800)
 			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.5f, 0,  1.75f * e, 5000);
 		break;
-	case PET: //!!! SCAFF (PET only used for testing):
+	case SCAFF:
 		writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width,     y_step * 2.f,  0,  1.75f * e, 4000)
 			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 3.f,  0,  2.34f * e, 4600)
 			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 3.f,  0,  2.63f * e, 5200);
@@ -860,85 +849,83 @@ void WipeTowerPrusaMM::toolchange_Unload(
 			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.2f, e0, 1.65f * e, 4600)
 			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, e0, 1.74f * e, 5200);
 	}*/
-
+}
 	// Pull the filament end into a cooling tube.
 	writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
 
-	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);
 
-	// In case the current print head position is closer to the left edge, reverse the direction.
-	if (std::abs(writer.x() - xl) < std::abs(writer.x() - xr))
-		std::swap(xl, xr);
-	// Horizontal cooling moves will be performed at the following Y coordinate:
-	/*writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
-		  .suppress_preview();*/
-
-	/*constexpr float min_cool_speed = 1600;
-	constexpr float max_cool_speed = 2400;
-
-	const int num_of_intervals = 4;
-
-	writer.travel(writer.x(), writer.y() + y_step)
-		  .retract(-3,min_cool_speed)
-		  .suppress_preview();
-
-	int filament_direction = -1;			// retract first
-	float cool_speed = min_cool_speed;
-	// Numbers 12 (24) below fix velocity in E to 12 times less than in X (total move in X assumed to be >> move in E)
+// cooling:
+	writer.retract(2, 2000);
+	writer.suppress_preview();
+	writer.travel(writer.x(), writer.y() + y_step);
+	const float start_x = writer.x();
+	const float turning_point = ( xr-start_x > start_x-xl ? xr : xl );
+	const float max_x_dist = 2*std::abs(start_x-turning_point);
+	const int N = 4 + (cooling_time-14)/3;
+	float time = cooling_time / N;
 	
-	for (int counter=0 ; counter < num_of_intervals ; ++counter) {
-		float needed_e_move = cool_speed * (cooling_time / num_of_intervals) / 12.f; // distance yet to move in E axis
-		do {			
-			remaining = ( left_to_right ? xr-m_perimeter_width-writer.x() : -xl-m_perimeter_width+writer.x()); // distance to the turning point
-			float xmove = std::min( std::fabs(needed_e_move) * 12.f, remaining * 2.f);						  // distance to travel
-			printf("posun z %f na %f",writer.x(),writer.x()+xmove / 2.f * (left_to_right ? 1 : -1));
-				writer.cool(writer.x() + xmove / 2.f * (left_to_right ? 1 : -1), writer.x(), filament_direction * xmove / 24.f, filament_direction * xmove / 24.f, cool_speed);
-			printf(", E %f, rychlost %f\n\n",filament_direction * xmove /12.f,cool_speed);
-			needed_e_move -= xmove/12.f;
-			if (needed_e_move > WT_EPSILON)
-				filament_direction *= -1;
-			else
-				left_to_right = !left_to_right;
-
-		} while (needed_e_move > WT_EPSILON);
-		if (filament_direction==-1)
-			cool_speed += (max_cool_speed - min_cool_speed) / num_of_intervals;
+	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,10.f); // distance to travel
+		
+		if (speed * time < 10.f) { 	// this move is the last one at this speed
+			++i;
+			time = cooling_time / N;			
+		}
+		else
+			time -= e_dist / speed; // subtract time this part will really take
+		
+		// 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.retract(-2, 2000);
+	writer.travel(writer.x(), writer.y() - y_step,2400);
 
-	writer.retract(3, max_cool_speed);*/
-
-	
-	/*switch (current_material)
+	// Alex's old cooling:
 	{
-	case PVA:
-		writer.cool(xl, xr, 3, -5, 1600)
-			  .cool(xl, xr, 5, -5, 2000)
-			  .cool(xl, xr, 5, -5, 2200)
-			  .cool(xl, xr, 5, -5, 2400)
-			  .cool(xl, xr, 5, -5, 2400)
-			  .cool(xl, xr, 5, -3, 2400);
-		break;
-	case SCAFF:
-		writer.cool(xl, xr, 3, -5, 1600)
-			  .cool(xl, xr, 5, -5, 2000)
-			  .cool(xl, xr, 5, -5, 2200)
-			  .cool(xl, xr, 5, -5, 2200)
-			  .cool(xl, xr, 5, -3, 2400);
-		break;
-	default:
-		writer.cool(xl, xr, 3, -5, 1600)
-			  .cool(xl, xr, 5, -5, 2000)
-			  .cool(xl, xr, 5, -5, 2400)
-			  .cool(xl, xr, 5, -3, 2400);
-	}*/
+		// In case the current print head position is closer to the left edge, reverse the direction.
+		/*if (std::abs(writer.x() - xl) < std::abs(writer.x() - xr))
+		std::swap(xl, xr);
+		// Horizontal cooling moves will be performed at the following Y coordinate:
+		writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
+		  .suppress_preview();
+		switch (current_material)
+		{
+		case PVA:
+			writer.cool(xl, xr, 3, -5, 1600)
+				.cool(xl, xr, 5, -5, 2000)
+				.cool(xl, xr, 5, -5, 2200)
+				.cool(xl, xr, 5, -5, 2400)
+				.cool(xl, xr, 5, -5, 2400)
+				.cool(xl, xr, 5, -3, 2400);
+			break;
+		case SCAFF:
+			writer.cool(xl, xr, 3, -5, 1600)
+				.cool(xl, xr, 5, -5, 2000)
+				.cool(xl, xr, 5, -5, 2200)
+				.cool(xl, xr, 5, -5, 2200)
+				.cool(xl, xr, 5, -3, 2400);
+			break;
+		default:
+			writer.cool(xl, xr, 3, -5, 1600)
+				.cool(xl, xr, 5, -5, 2000)
+				.cool(xl, xr, 5, -5, 2400)
+				.cool(xl, xr, 5, -3, 2400);
+		}*/
+}
+	// this is to align ramming and future loading extrusions, so the future y-steps can be uniform from the start:
+	writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_line_width) / 2.f, 2400.f);
 
 	writer.resume_preview()
 		  .flush_planner_queue();
 }
 
-// Change the tool, set a speed override for solube and flex materials.
+// Change the tool, set a speed override for soluble and flex materials.
 void WipeTowerPrusaMM::toolchange_Change(
 	PrusaMultiMaterial::Writer &writer,
 	const unsigned int 	new_tool, 
@@ -958,17 +945,18 @@ void WipeTowerPrusaMM::toolchange_Change(
 	m_current_tool = new_tool;
 }
 
+
+
 void WipeTowerPrusaMM::toolchange_Load(
 	PrusaMultiMaterial::Writer &writer,
 	const box_coordinates  &cleaning_box)
-{
+{	
 	float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f;
 	float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f;
-	//FIXME flipping left / right side, so that the following toolchange_Wipe will start
-	// where toolchange_Load ends. 
-	//std::swap(xl, xr);
+	float oldx=writer.x();	// the nozzle is in place to do the first wiping moves, we will remember the position
+	float oldy=writer.y();
 
-	/*writer.append("; CP TOOLCHANGE LOAD\n")
+	writer.append("; CP TOOLCHANGE LOAD\n")
 	// Load the filament while moving left / right,
 	// so the excess material will not create a blob at a single position.
 		  .suppress_preview()
@@ -979,8 +967,11 @@ void WipeTowerPrusaMM::toolchange_Load(
 		  // Slowing down
 		  .load_move_x(xr, 20, 1600)
 		  .load_move_x(xl, 10, 1000)
+		  .travel(oldx,oldy)
 		  .resume_preview();
-*/
+
+// Alex's old loading extrusions - this has been moved to toolchange_Wipe(...)
+{
 	// Extrude first five lines (just three lines if colorInit is set).
 	/*writer.extrude(xr, writer.y(), 1600);
 	bool   colorInit = false;
@@ -991,24 +982,8 @@ void WipeTowerPrusaMM::toolchange_Load(
 		writer.extrude(xl, writer.y(), 		2200);
 		writer.travel (xl, writer.y() + dy, 7200);
 	 	writer.extrude(xr, writer.y(), 		2200);
-	}*/
-
-	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
-	float x = (loading_volume) / (Filament_Area * m_extrusion_flow); // extrusion length to extrude desired volume
-	while (x > WT_EPSILON) {		
-		float remaining = (m_left_to_right ? xr - writer.x() : writer.x() - xl );
-		float dist = std::min(x, remaining);
-		writer.extrude(writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, writer.y(), 2200);
-		x -= dist;
-		
-		if (x > WT_EPSILON) {	// don't switch it for the last (unfinished) line
-			m_left_to_right = !m_left_to_right;
-			writer.travel(writer.x(), writer.y() + dy, 7200);
-		}
-		else	// advance just a fraction for debugging (to see where the load ends) (FIXME - HAS to be deleted, can pass the edge)
-			writer.travel(writer.x()+(m_left_to_right ? 0.1f : -0.1f),writer.y());
-	}
-
+	}*/	
+}
 	// Reset the extruder current to the normal value.
 	writer.set_extruder_trimpot(550);
 }
@@ -1020,40 +995,55 @@ void WipeTowerPrusaMM::toolchange_Load(
 void WipeTowerPrusaMM::toolchange_Wipe(
 	PrusaMultiMaterial::Writer &writer,
 	const box_coordinates  &cleaning_box,
-	bool skip_initial_y_move)
+	float wipe_volume)
 {
 	// Increase flow on first layer, slow down print.
 	writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f))
 		  .append("; CP TOOLCHANGE WIPE\n");
 	float wipe_coeff = m_is_first_layer ? 0.5f : 1.f;
-	const float& xl = cleaning_box.ld.x; // + 2.f * m_perimeter_width;
-	const float& xr = cleaning_box.rd.x; // - 2.f * m_perimeter_width;
-	// Wipe speed will increase up to 4800.
-	float wipe_speed 	 = 4200.f;
-	float wipe_speed_inc = 50.f;
-	float wipe_speed_max = 4800.f;
-	// Y increment per wipe line.
-	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
+	const float& xl = cleaning_box.ld.x;
+	const float& xr = cleaning_box.rd.x;
+	
+	// DEBUGGING: The function makes sure it always wipes at least the ordered volume, even if it means violating
+	//            the perimeter. This can later be removed and simply wipe until the end of the assigned area.
+	//			  (Variables x_to_wipe and traversed_x)
 
-	for ( int i = 0 ; true ; ++i )
-	{
-		wipe_speed = std::min(wipe_speed_max, wipe_speed + wipe_speed_inc);
-		if (m_left_to_right)
-			writer.extrude(xr - (i % 4 == 0 ? 0 : m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
-		else
-			writer.extrude(xl + (i % 4 == 1 ? 0 : m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
+	float x_to_wipe = volume_to_length(wipe_volume, m_line_width, m_layer_height);
+	float dy = m_extra_spacing*m_line_width;
+	float wipe_speed = 1600.f;
+
+	for (int i = 0; true; ++i)	{
+		if (i!=0) {
+			if (wipe_speed < 1610.f) wipe_speed = 1800.f;
+			else if (wipe_speed < 1810.f) wipe_speed = 2200.f;
+			else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
+			else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
+		}
 		
-		if ((m_current_shape == SHAPE_NORMAL) ?		// in case next line would not fit
+		float traversed_x = writer.x();
+		if (m_left_to_right)
+			writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
+		else
+			writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
+		
+		/*if ((m_current_shape == SHAPE_NORMAL) ?		// in case next line would not fit
 			(writer.y() > cleaning_box.lu.y - m_perimeter_width * 1.5f) :
 			(writer.y() < cleaning_box.ld.y + m_perimeter_width * 1.5f))
+			break;*/
+		traversed_x -= writer.x();
+		x_to_wipe -= fabs(traversed_x);
+		if (x_to_wipe < WT_EPSILON) {
+			writer.travel(m_left_to_right ? xl + 1.5*m_perimeter_width : xr - 1.5*m_perimeter_width, writer.y(), 7200);
 			break;
-
+		}
 		// stepping to the next line:
-		writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * m_perimeter_width, writer.y() + dy);
+		writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5*m_perimeter_width, writer.y() + dy);
 		m_left_to_right = !m_left_to_right;
 	}
-	// Reset the extrusion flow.
-	writer.set_extrusion_flow(m_extrusion_flow);
+	writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
+
+	// Wipe the nozzle
+
 }
 
 
@@ -1071,6 +1061,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 		.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)
 		.append(";--------------------\n"
 				"; CP EMPTY GRID START\n")
 		// m_num_layer_changes is incremented by set_z, so it is 1 based.
@@ -1078,18 +1069,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 
 	// Slow down on the 1st layer.
 	float speed_factor = m_is_first_layer ? 0.5f : 1.f;
-
-	float current_depth = m_wipe_tower_depth;
-	if (m_is_first_layer)
-		current_depth = m_wipe_tower_depth;
-	else 
-		current_depth = m_layer_info->depth;
-
-
-	box_coordinates fill_box(m_wipe_tower_pos + xy(0.f, m_current_wipe_start_y),
-		m_wipe_tower_width, current_depth - m_current_wipe_start_y);
-	fill_box.expand(0.f, - 0.5f * m_perimeter_width);
-	
+	float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
+	box_coordinates fill_box(m_wipe_tower_pos + xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
+							 m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
 
 	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
 		if ( m_layer_info->tool_changes.size() == 0) {
@@ -1106,102 +1088,62 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 		}
 	} else {
 		// The print head is inside the wipe tower. Rather move to the start of the following extrusion.
-		// writer.set_initial_position(fill_box.ld);
 		writer.set_initial_position(fill_box.ld);
 	}
 
 	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		// Extrude the first perimeter.
 		box_coordinates box = fill_box;
-		writer.extrude(box.lu, 2400 * speed_factor)
-			  .extrude(box.ru)
-			  .extrude(box.rd)
-			  .extrude(box.ld + xy(m_perimeter_width / 2, 0));
+		for (int i=0;i<2;++i) {
+			if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer
+				if (i==0) box.expand(m_perimeter_width);
+				else box.expand(-m_perimeter_width);
+			}
+			else i=2;	// only draw the inner perimeter
 
-		// Extrude second perimeter.
-		box.expand(- m_perimeter_width / 2);
-		writer.extrude(box.lu, 3200 * speed_factor)
-			  .extrude(box.ru)
-			  .extrude(box.rd)
-			  .extrude(box.ld + xy(m_perimeter_width / 2, 0));
+			writer.travel(box.ld,7200)
+			    .extrude(box.lu, 2400 * speed_factor)
+			    .extrude(box.ru)
+			    .extrude(box.rd)
+			    .extrude(box.ld);
+		}
 
-		if (m_is_first_layer) {
+		if (m_is_first_layer && improve_first_layer_adhesion) {
 			// Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
-			box.expand(- m_perimeter_width / 2);
-			box.ld.y -= 0.5f * m_perimeter_width;
-			box.rd.y  = box.ld.y;
-			int   nsteps = int(floor((box.lu.y - box.ld.y) / (2. * (1.0 * m_perimeter_width))));
+			box.expand(-m_perimeter_width/2.f);
+			unsigned nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
 			float step   = (box.lu.y - box.ld.y) / nsteps;
-			//FIXME:
+			writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f));
 			if (nsteps >= 0)
 				for (size_t i = 0; i < nsteps; ++i)	{
-					writer.extrude(box.ld.x, writer.y() + 0.5f * step);
-					writer.extrude(box.rd.x, writer.y());
-					writer.extrude(box.rd.x, writer.y() + 0.5f * step);
-					writer.extrude(box.ld.x, writer.y());
+					writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step);
+					writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y());
+					writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
+					writer.extrude(box.ld.x + m_perimeter_width / 2.f, writer.y());
 				}
+				writer.travel(box.rd.x-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
 		} else {
 			// Extrude a sparse infill to support the material to be printed above.
 
-			// Extrude an inverse U at the left of the region.
-			writer.extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2))
-				  .extrude(fill_box.ld + xy(m_perimeter_width * 3,   m_perimeter_width), 2900 * speed_factor)
-			      .extrude(fill_box.lu + xy(m_perimeter_width * 3, - m_perimeter_width))
-				  .extrude(fill_box.lu + xy(m_perimeter_width * 6, - m_perimeter_width))
-				  .extrude(fill_box.ld + xy(m_perimeter_width * 6,   m_perimeter_width));
-
-			/*if (fill_box.lu.y - fill_box.ld.y > 4.f) {
-				const float max_bridge_distance = 10.f; // in mm
-				size_t zig_zags_num = int((m_wipe_tower_width - m_perimeter_width * 12.f) / ( max_bridge_distance * 2.f ) ) + 1 ;
-				float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / (zig_zags_num * 4);
-				for (size_t i = 0; i < zig_zags_num; ++ i) {
-					writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width * 8, 3200 * speed_factor);
-					writer.extrude(writer.x()       , fill_box.lu.y - m_perimeter_width * 8);
-					writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width    );
-					writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width * 8);
-					writer.extrude(writer.x()       , fill_box.ld.y + m_perimeter_width * 8);
-					writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width    );
-				}
-			}
-			// Extrude an inverse U at the left of the region.
-			writer.extrude(fill_box.ru + xy(-m_perimeter_width * 6, -m_perimeter_width), 2900 * speed_factor)
-				  .extrude(fill_box.ru + xy(-m_perimeter_width * 3, -m_perimeter_width))
-				  .extrude(fill_box.rd + xy(-m_perimeter_width * 3, m_perimeter_width))
-				  .extrude(fill_box.rd + xy(-m_perimeter_width, m_perimeter_width));*/
-
-			const float dy = (fill_box.lu.y - fill_box.ld.y - 2.f * m_perimeter_width);
+			const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width);
 			if (dy > m_perimeter_width)
-			{				
-				const float max_bridge_distance = 10.f; // in mm
-				const size_t zig_zags_num = (fill_box.rd.x - fill_box.ld.x - m_perimeter_width * 12.f) / max_bridge_distance;
-				const float step = (fill_box.rd.x - fill_box.ld.x - m_perimeter_width * 12.f) / (float)zig_zags_num;
+			{
+				// Extrude an inverse U at the left of the region.
+				writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f))
+					  .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
 
-				float offsetx = std::max(0.f, dy / m_last_infill_tan > step / 2.f ? step - dy / m_last_infill_tan : 0.f);
-				float offsety = ( offsetx != 0 ? 0 : std::max(0.f,  dy - m_last_infill_tan * (step - m_perimeter_width)) );
-				if (offsety < m_last_infill_tan * m_perimeter_width + WT_EPSILON || offsety > dy / 2.f)
-					offsety = 0.f;
-				
-				
-				for (size_t i = 0; i < zig_zags_num; ++i)
-				{
-					writer.extrude(writer.x() + offsetx, writer.y(), 3200 * speed_factor);
-					if ( offsety != 0 ) {
-						writer.extrude(writer.x() + m_perimeter_width, writer.y());
-						writer.extrude(writer.x(), writer.y() + offsety);
-					}
-					writer.extrude(writer.x() + step - offsetx - (offsety==0 ? 0 : m_perimeter_width), fill_box.lu.y - m_perimeter_width);
-					writer.extrude(writer.x() , fill_box.ld.y + m_perimeter_width);
+				const float left = fill_box.lu.x+2*m_perimeter_width;
+				const float right = fill_box.ru.x - 2 * m_perimeter_width;
+				const int n = 1+(right-left)/max_bridge_distance;
+				const float dx = (right-left)/n;
+				for (int i=1;i<=n;++i) {
+					float x=left+dx*i;
+					writer.travel(x,writer.y());
+					writer.extrude(x,i%2 ? fill_box.rd.y : fill_box.ru.y);					
 				}
-				m_last_infill_tan = (dy - offsety) / (step - offsetx - (offsety==0 ? 0 : m_perimeter_width));
+				writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
 			}
-
-			// Extrude an inverse U at the left of the region.
-			writer.extrude(fill_box.rd + xy(-m_perimeter_width * 6, m_perimeter_width), 2900 * speed_factor)
-				  .extrude(fill_box.rd + xy(-m_perimeter_width * 3, m_perimeter_width))
-				  .extrude(fill_box.ru + xy(-m_perimeter_width * 3, -m_perimeter_width))
-				  .extrude(fill_box.ru + xy(-m_perimeter_width, -m_perimeter_width));
 		}
-
+/*
 		// if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
 		if (true)
 	       	// Wipe along the front side of the current wiping box.
@@ -1210,16 +1152,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 			  	  .travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2));
 		else
 			writer.feedrate(7200);
-
+*/
 		writer.append("; CP EMPTY GRID END\n"
 				      ";------------------\n\n\n\n\n\n\n");
 
 		// Indicate that this wipe tower layer is fully covered.
-		m_current_wipe_start_y = m_wipe_tower_depth;
-	    ///m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;
-		
+		m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
+	    ///m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;		
 	}
-
 	ToolChangeResult result;
 	result.print_z 	  	= this->m_z_pos;
 	result.layer_height = this->m_layer_height;
@@ -1232,23 +1172,20 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 }
 
 // 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)
-{
-	assert(m_plan.back().z <= z_par);	// refuse to add a layer below the last one
-		
-	// volumes in mm^3 required for wipe: {{from 0 to ...},{from 1 to ...},{from 2 to ...},{from 3 to ...}}, usage [from][to]
-	const std::vector<std::vector<float>> wipe_volumes = {{  0, 40, 60,100},
-														  {100,  0,130,100},
-														  { 80, 90,  0,110},
-														  { 50, 40, 60,  0}};
+{	
+	assert(m_plan.back().z <= z_par + WT_EPSILON );	// refuses to add a layer below the last one
 
-	float depth = (wipe_volumes[old_tool][new_tool]) / (extrusion_flow(layer_height_par) * Filament_Area); // length of extrusion
-	depth += 6 * 59; // reserved for ramming
-	depth += 2 * 59; // reserved for loading
-	depth -= 2 * 59; // we will also print borders
-	depth = floor(depth / m_wipe_tower_width + 1);	// number of lines to extrude
-	depth *= m_line_width; // conversion to distance
+	float width = m_wipe_tower_width - 3*m_perimeter_width; 
+	float length_to_extrude = volume_to_length(0.25f * std::accumulate(ramming_speed.begin(), ramming_speed.end(), 0.f),
+										  m_line_width * ramming_line_width_multiplicator,
+										  layer_height_par);
+	float depth = (int(length_to_extrude / width) + 1) * (m_line_width * ramming_line_width_multiplicator * ramming_step_multiplicator);
+	length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
+	length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
+	length_to_extrude = std::max(length_to_extrude,0.f);
+	depth += (int(length_to_extrude / width) + 1) * m_line_width;
+	depth *= m_extra_spacing;
 
 	if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan along with the first toolchange
 		m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
@@ -1259,33 +1196,97 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 		m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth));
 	}
 
-	
-
-	// Calculate m_wipe_tower (maximum depth for all the layers) and propagate depths downwards
-	// Could be only called when a layer is changed, but do not forget about the last one
-	float this_layer_depth = m_plan.back().toolchanges_depth();
+	// Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
+	/*float this_layer_depth = m_plan.back().toolchanges_depth();
 	m_plan.back().depth = this_layer_depth;
 	
-	if (this_layer_depth > m_wipe_tower_depth)
-		m_wipe_tower_depth = this_layer_depth;
+	if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
+		m_wipe_tower_depth = this_layer_depth + m_perimeter_width;
 	for (int i = m_plan.size() - 2; i >= 0 && m_plan[i].depth < this_layer_depth; i-- ) {
 		if ( this_layer_depth - m_plan[i].depth < min_layer_difference && !m_plan[i].tool_changes.empty())
-			m_plan[i].tool_changes.back().required_depth += this_layer_depth - m_plan[i].depth;
+			m_plan[i].depth += this_layer_depth - m_plan[i].depth;
 		m_plan[i].depth = this_layer_depth;
+	}*/
+}
+
+
+
+void WipeTowerPrusaMM::plan_tower()
+{
+	// Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
+	m_wipe_tower_depth = 0.f;
+	for (auto& layer : m_plan)
+		layer.depth = 0.f;
+	
+	for (int layer_index = m_plan.size() - 1; layer_index >= 0; --layer_index)
+	{
+		float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth());
+		m_plan[layer_index].depth = this_layer_depth;
+		
+		if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
+			m_wipe_tower_depth = this_layer_depth + m_perimeter_width;
+
+		for (int i = layer_index - 1; i >= 0 /*&& m_plan[i].depth < this_layer_depth*/; i--)
+		{
+			if (m_plan[i].depth - this_layer_depth < min_layer_difference )
+				m_plan[i].depth = this_layer_depth;
+		}
 	}
 }
 
 
-// Processes vector m_plan and calls respective function to generate G-code for the wipe tower
-// Resulting ToolChangeResults are appended into container passed by "result" reference
+void WipeTowerPrusaMM::make_wipe_tower_square()
+{
+	const float width = m_wipe_tower_width - 3 * m_perimeter_width;
+	const float depth = m_wipe_tower_depth - m_perimeter_width;
+	// area that we actually print into is width*depth
+	float side = sqrt(depth * width);
+
+	m_wipe_tower_width = side + 3 * m_perimeter_width;
+	m_wipe_tower_depth = side + 2 * m_perimeter_width;
+	// For all layers, find how depth changed and update all toolchange depths
+	for (auto &lay : m_plan)
+	{
+		side = sqrt(lay.depth * width);
+		float width_ratio = width / side;
+
+		//lay.extra_spacing = width_ratio;
+		for (auto &tch : lay.tool_changes)
+			tch.required_depth *= width_ratio;
+	}
+
+	plan_tower();				// propagates depth downwards again (width has changed)
+	for (auto& lay : m_plan)	// depths set, now the spacing
+		lay.extra_spacing = lay.depth / lay.toolchanges_depth();
+
+}
+
+
+// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
+// Resulting ToolChangeResults are appended into vector "result"
 void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
 {
+	m_extra_spacing = 1.f;
+
+	plan_tower();
+	if (peters_wipe_tower)
+			make_wipe_tower_square();
+
 	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 (peters_wipe_tower)
+			m_wipe_tower_rotation_angle += 90.f;
+		else
+			m_wipe_tower_rotation_angle += 180.f;
+		if (!peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
+			m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
+
+
+
 		for (const auto &toolchange : layer.tool_changes)
 			layer_result.emplace_back(tool_change(toolchange.new_tool, false, WipeTower::PURPOSE_EXTRUDE));
 
@@ -1307,8 +1308,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 				layer_result.pop_back();
 			}			
 		}
-		else
-			m_last_infill_tan = 1000.f; //if the layer was dense, no worries about infill continuity on the next
 
 		result.emplace_back(std::move(layer_result));
 		m_is_first_layer = false;
@@ -1316,5 +1315,4 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 }
 
 
-
 }; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 6ca807528..9ed7ae128 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -9,20 +9,17 @@
 #include "WipeTower.hpp"
 
 // Following is used to calculate extrusion flow - should be taken from config in future
-constexpr float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament diameter in mm
+constexpr float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^3
 constexpr float Nozzle_Diameter = 0.4f;	// nozzle diameter in mm
 // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
-constexpr float Width_To_Nozzle_Ratio = 1.f;
-// m_perimeter_width was hardcoded until now as 0.5 (for 0.4 nozzle and 0.2 layer height)
-// Konst = 1.25 implies same result
-constexpr float Konst = 1.25f;
+constexpr float Width_To_Nozzle_Ratio = 1.25f;
 
-// m_perimeter_width is used in plan_toolchange - take care of proper initialization value when changing to variable
+// m_perimeter_width was hardcoded until now as 0.5 (for 0.4 nozzle and 0.2 layer height)
+// FIXME m_perimeter_width is used in plan_toolchange - take care of proper initialization value when changing to variable
+constexpr float Konst = 1.f;
 constexpr float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Konst;
 
 constexpr float WT_EPSILON = 1e-3f;
-constexpr float min_layer_difference = 2.f;
-
 
 
 namespace Slic3r
@@ -59,9 +56,11 @@ public:
 	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, unsigned int initial_tool) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
-		m_wipe_area(wipe_area),
 		m_wipe_tower_rotation_angle(rotation_angle),
+		m_y_shift(0.f),
 		m_z_pos(0.f),
+		m_is_first_layer(false),
+		m_is_last_layer(false),		
 		m_current_tool(initial_tool)
  	{
 		for (size_t i = 0; i < 4; ++ i) {
@@ -96,6 +95,10 @@ public:
 
 	void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
 
+	void plan_tower();
+
+	void make_wipe_tower_square();
+
 	// Switch to a next layer.
 	virtual void set_layer(
 		// Print height of this layer.
@@ -115,18 +118,10 @@ public:
 		// Start counting the color changes from zero. Special case: -1 - extrude a brim first.
 		///m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
 		m_print_brim = is_first_layer;
-		m_current_wipe_start_y  = 0.f;
+		m_depth_traversed  = 0.f; // to make room for perimeter line
 		m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
+		
 		++ m_num_layer_changes;
-
-
-		///m_max_color_changes 	= max_tool_changes;
-		///m_is_last_layer			= is_last_layer;
-		// Extrusion rate for an extrusion aka perimeter width 0.35mm.
-		// Clamp the extrusion height to a 0.2mm layer height, independent of the nozzle diameter.
-//		m_extrusion_flow = std::min(0.2f, layer_height) * 0.145f;
-		// Use a strictly
-		//m_extrusion_flow = layer_height * 0.145f;
 		
 		// Calculates extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height
 		m_extrusion_flow = extrusion_flow(layer_height);
@@ -135,6 +130,7 @@ public:
 		m_layer_info = nullptr;
 		for (auto &a : m_plan)
 			if ( a.z > print_z - WT_EPSILON && a.z < print_z + WT_EPSILON ) {
+				m_extra_spacing = a.extra_spacing;
 				m_layer_info = &a;
 				break;
 			}
@@ -171,49 +167,36 @@ public:
 	// the wipe tower has been completely covered by the tool change extrusions,
 	// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
 	virtual bool 			 layer_finished() const {
-		return ( (m_is_first_layer ? m_wipe_tower_depth : m_layer_info->depth) - WT_EPSILON < m_current_wipe_start_y);
+		return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
 	}
 
 
 private:
 	WipeTowerPrusaMM();
 
-	// A fill-in direction (positive Y, negative Y) alternates with each layer.
-	enum wipe_shape
+	enum wipe_shape // A fill-in direction (positive Y, negative Y) alternates with each layer.
 	{
-		SHAPE_NORMAL   = 1,
+		SHAPE_NORMAL = 1,
 		SHAPE_REVERSED = -1
 	};
 
-	// Left front corner of the wipe tower in mm.
-	xy     			m_wipe_tower_pos;
-	// Width of the wipe tower.
-	float  			m_wipe_tower_width;
-	// Depth of the wipe tower (wipe_area * max_color_changes at the base)
-	float			m_wipe_tower_depth = 0.f;
-	// Per color Y span.
-	float  			m_wipe_area;
-	// Wipe tower rotation angle in degrees (with respect to x axis
-	float			m_wipe_tower_rotation_angle;
-	// Current Z position.
-	float  			m_z_pos 			= 0.f;
-	// Current layer height.
-	float           m_layer_height      = 0.f;
-	// Maximum number of color changes per layer.
-	size_t 			m_max_color_changes = 0;
-	// Is this the 1st layer of the print? If so, print the brim around the waste tower.
-	bool   			m_is_first_layer = false;
-	// Is this the last layer of this waste tower?
-	bool   			m_is_last_layer  = false;
+	xy 	   m_wipe_tower_pos; 			// Left front corner of the wipe tower in mm.
+	float  m_wipe_tower_width; 			// Width of the wipe tower.
+	float  m_wipe_tower_depth 	= 0.f; 	// Depth of the wipe tower
+	float  m_wipe_tower_rotation_angle; // Wipe tower rotation angle in degrees (with respect to x axis
+	float  m_y_shift			= 0.f;  // y shift passed to writer
+	float  m_z_pos 				= 0.f;  // Current Z position.
+	float  m_layer_height 		= 0.f; 	// Current layer height.
+	size_t m_max_color_changes 	= 0; 	// Maximum number of color changes per layer.
+	bool   m_is_first_layer 	= false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
+	bool   m_is_last_layer 		= false;// Is this the last layer of this waste tower?
 
 	// G-code generator parameters.
 	float  			m_zhop 			 = 0.5f;
 	float  			m_retract		 = 4.f;
-	// Width of an extrusion line, also a perimeter spacing for 100% infill.
-	float  			m_line_width = Nozzle_Diameter * Width_To_Nozzle_Ratio;
 	
-	// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
-	float  			m_extrusion_flow  = 0.029f;
+	float m_line_width = Nozzle_Diameter * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
+	float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
 
 	// Extruder specific parameters.
 	material_type 	m_material[4];
@@ -221,23 +204,20 @@ private:
 	int  			m_first_layer_temperature[4];
 
 	// State of the wiper tower generator.
-	// Layer change counter for the output statistics.
-	unsigned int 	m_num_layer_changes = 0;
-	// Tool change change counter for the output statistics.
-	unsigned int 	m_num_tool_changes = 0;
-	// Layer change counter in this layer. Counting up to m_max_color_changes.
-	///unsigned int 	m_idx_tool_change_in_layer = 0;
+	unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
+	unsigned int m_num_tool_changes  = 0; // Tool change change counter for the output statistics.
+	///unsigned int 	m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
 	bool m_print_brim = true;
 	// A fill-in direction (positive Y, negative Y) alternates with each layer.
 	wipe_shape   	m_current_shape = SHAPE_NORMAL;
 	unsigned int 	m_current_tool  = 0;
-	// Current y position at the wipe tower.
-	float 		 	m_current_wipe_start_y = 0.f;
+
+	float m_depth_traversed = 0.f; // Current y position at the wipe tower.
 	// How much to wipe the 1st extruder over the wipe tower at the 1st layer
 	// after the wipe tower brim has been extruded?
 	float  			m_initial_extra_wipe = 0.f;
-	float			m_last_infill_tan = 1000.f;	// impossibly high value
 	bool 			m_left_to_right = true;
+	float			m_extra_spacing = 1.f;
 
 	float extrusion_flow(float layer_height = -1.f) const
 	{
@@ -246,7 +226,11 @@ private:
 		return layer_height * ( Width_To_Nozzle_Ratio * Nozzle_Diameter - layer_height * (1-M_PI/4.f)) / (Filament_Area);
 	}
 
-	
+	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.)));
+	}
+
+
 	struct box_coordinates
 	{
 		box_coordinates(float left, float bottom, float width, float height) :
@@ -274,8 +258,8 @@ private:
 		}
 		xy ld;  // left down
 		xy lu;	// left upper 
-		xy ru;	// right upper
 		xy rd;	// right lower
+		xy ru;  // right upper
 	};
 
 
@@ -290,12 +274,13 @@ private:
 		float z;		// z position of the layer
 		float height;	// layer height
 		float depth;	// depth of the layer based on all layers above
+		float extra_spacing;
 		float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
 
 		std::vector<ToolChange> tool_changes;
 
 		WipeTowerInfo(float z_par, float layer_height_par)
-			: z{z_par}, height{layer_height_par} { }
+			: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
 	};
 
 	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
@@ -327,7 +312,7 @@ private:
 	void toolchange_Wipe(
 		PrusaMultiMaterial::Writer &writer,
 		const box_coordinates  &cleaning_box,
-		bool skip_initial_y_move);	
+		float wipe_volume);	
 };
 
 }; // namespace Slic3r

From 5a02bde1705bb86674e651288af3616e3f194566 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 22 Feb 2018 09:28:31 +0100
Subject: [PATCH 013/118] Fix of merge conflict and uninitialized variables in
 writer class

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 4 ++--
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 2 +-
 xs/src/slic3r/GUI/3DScene.cpp               | 8 ++++----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index adb19eba9..c5b0d6044 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -366,8 +366,8 @@ private:
 	float   	  m_angle_deg = 0.f;
 	float		  m_y_shift = 0.f;
 	WipeTower::xy m_wipe_tower_pos;
-	float 		  m_wipe_tower_width;
-	float		  m_wipe_tower_depth;
+	float 		  m_wipe_tower_width = 0.f;
+	float		  m_wipe_tower_depth = 0.f;
 	float		  m_last_fan_speed = 0.f;
 
 		std::string
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 9ed7ae128..ea704694a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -183,7 +183,7 @@ private:
 	xy 	   m_wipe_tower_pos; 			// Left front corner of the wipe tower in mm.
 	float  m_wipe_tower_width; 			// Width of the wipe tower.
 	float  m_wipe_tower_depth 	= 0.f; 	// Depth of the wipe tower
-	float  m_wipe_tower_rotation_angle; // Wipe tower rotation angle in degrees (with respect to x axis
+	float  m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis
 	float  m_y_shift			= 0.f;  // y shift passed to writer
 	float  m_z_pos 				= 0.f;  // Current Z position.
 	float  m_layer_height 		= 0.f; 	// Current layer height.
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index cfc0947fe..74ec515c1 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -321,11 +321,11 @@ int GLVolumeCollection::load_wipe_tower_preview(
     Point origin_of_rotation(0.f,0.f);
     mesh.rotate(rotation_angle,&origin_of_rotation);
 
-	v.indexed_vertex_array.load_mesh_flat_shading(mesh);
-	v.origin = Pointf3(pos_x, pos_y, 0.);
+    v.indexed_vertex_array.load_mesh_flat_shading(mesh);
+    v.origin = Pointf3(pos_x, pos_y, 0.);
     // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
     v.bounding_box = v.indexed_vertex_array.bounding_box();
-	v.indexed_vertex_array.finalize_geometry(use_VBOs);
+    v.indexed_vertex_array.finalize_geometry(use_VBOs);
     v.composite_id = obj_idx * 1000000;
     v.select_group_id = obj_idx * 1000000;
     v.drag_group_id = obj_idx * 1000;
@@ -2291,7 +2291,7 @@ void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, boo
     const PrintConfig& config = print.config;
     unsigned int extruders_count = config.nozzle_diameter.size();
     if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects)
-        volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, config.wipe_tower_per_color_wipe * (extruders_count - 1), max_z, use_VBOs);
+        volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, config.wipe_tower_per_color_wipe * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs);
 }
 
 }

From 5f5a59328b243e25439a7241ab8403e8570352f6 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 22 Feb 2018 11:03:29 +0100
Subject: [PATCH 014/118] Minor refactoring, actualized comments, etc.

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 35 +++++++++++++--------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 33 +++++++++----------
 2 files changed, 36 insertions(+), 32 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index c5b0d6044..2a78ed6f1 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -1193,26 +1193,32 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 {	
 	assert(m_plan.back().z <= z_par + WT_EPSILON );	// refuses to add a layer below the last one
 
+	if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan first
+		m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
+
+	if (brim) {	// this toolchange prints brim - we must add it to m_plan, but not to count its depth
+		m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool));
+		return;
+	}
+
+	if (old_tool==new_tool)	// new layer without toolchanges - we are done
+		return;
+
+	// this is an actual toolchange - let's calculate depth to reserve on the wipe tower
+	float depth = 0.f;			
 	float width = m_wipe_tower_width - 3*m_perimeter_width; 
 	float length_to_extrude = volume_to_length(0.25f * std::accumulate(ramming_speed.begin(), ramming_speed.end(), 0.f),
-										  m_line_width * ramming_line_width_multiplicator,
-										  layer_height_par);
-	float depth = (int(length_to_extrude / width) + 1) * (m_line_width * ramming_line_width_multiplicator * ramming_step_multiplicator);
+										m_line_width * ramming_line_width_multiplicator,
+										layer_height_par);
+	depth = (int(length_to_extrude / width) + 1) * (m_line_width * ramming_line_width_multiplicator * ramming_step_multiplicator);
 	length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
 	length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
 	length_to_extrude = std::max(length_to_extrude,0.f);
 	depth += (int(length_to_extrude / width) + 1) * m_line_width;
-	depth *= m_extra_spacing;
-
-	if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan along with the first toolchange
-		m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
-
-	if ( brim || old_tool != new_tool )	{
-		if (brim) // this toolchange prints brim, we need it in m_plan, but not to count its depth
-			depth = 0.f;
-		m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth));
-	}
+	depth *= m_extra_spacing;	
 
+	m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth));
+	
 	// Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
 	/*float this_layer_depth = m_plan.back().toolchanges_depth();
 	m_plan.back().depth = this_layer_depth;
@@ -1283,6 +1289,9 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
 // Resulting ToolChangeResults are appended into vector "result"
 void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
 {
+	if (m_plan.empty())	return;
+	else m_layer_info = m_plan.begin();
+
 	m_extra_spacing = 1.f;
 
 	plan_tower();
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index ea704694a..f5fc2985e 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -93,10 +93,13 @@ public:
 	// 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);
 
+	// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
 	void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
 
+	// Calculates depth for all layers and propagates them downwards
 	void plan_tower();
 
+	// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
 	void make_wipe_tower_square();
 
 	// Switch to a next layer.
@@ -115,7 +118,7 @@ public:
 		m_z_pos 				= print_z;
 		m_layer_height			= layer_height;
 		m_is_first_layer 		= is_first_layer;
-		// Start counting the color changes from zero. Special case: -1 - extrude a brim first.
+		// Start counting the color changes from zero. Special case -1: extrude a brim first.
 		///m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
 		m_print_brim = is_first_layer;
 		m_depth_traversed  = 0.f; // to make room for perimeter line
@@ -126,14 +129,8 @@ public:
 		// Calculates extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height
 		m_extrusion_flow = extrusion_flow(layer_height);
 
-		// FIXME - ideally get rid of set_layer altogether and iterate through m_plan in generate(...)
-		m_layer_info = nullptr;
-		for (auto &a : m_plan)
-			if ( a.z > print_z - WT_EPSILON && a.z < print_z + WT_EPSILON ) {
-				m_extra_spacing = a.extra_spacing;
-				m_layer_info = &a;
-				break;
-			}
+		while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1!=m_plan.end())
+			++m_layer_info;
 	}
 
 	// Return the wipe tower position.
@@ -159,13 +156,11 @@ public:
 	// On the first layer, extrude a brim around the future wipe tower first.
 	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer, Purpose purpose);
 
-	// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
+	// Fill the unfilled space with a zig-zag.
 	// Call this method only if layer_finished() is false.
 	virtual ToolChangeResult finish_layer(Purpose purpose);
 
-	// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
-	// the wipe tower has been completely covered by the tool change extrusions,
-	// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
+	// Is the current layer finished?
 	virtual bool 			 layer_finished() const {
 		return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
 	}
@@ -219,13 +214,15 @@ private:
 	bool 			m_left_to_right = true;
 	float			m_extra_spacing = 1.f;
 
-	float extrusion_flow(float layer_height = -1.f) const
+	// Calculates extrusion flow needed to produce required line width for given layer height
+	float extrusion_flow(float layer_height = -1.f) const	// negative layer_height - return current m_extrusion_flow
 	{
 		if ( layer_height < 0 )
 			return m_extrusion_flow;
 		return layer_height * ( Width_To_Nozzle_Ratio * Nozzle_Diameter - layer_height * (1-M_PI/4.f)) / (Filament_Area);
 	}
 
+	// Calculates length of extrusion line to extrude given volume
 	float volume_to_length(float volume, float line_width, float layer_height) const {
 		return volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.)));
 	}
@@ -269,7 +266,7 @@ private:
 			unsigned int old_tool;
 			unsigned int new_tool;
 			float required_depth;
-			ToolChange(unsigned int old,unsigned int newtool,float depth) : old_tool{old}, new_tool{newtool}, required_depth{depth} {}
+			ToolChange(unsigned int old,unsigned int newtool,float depth=0.f) : old_tool{old}, new_tool{newtool}, required_depth{depth} {}
 		};
 		float z;		// z position of the layer
 		float height;	// layer height
@@ -283,10 +280,8 @@ private:
 			: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
 	};
 
-	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
-	std::vector<WipeTowerInfo> m_plan;
-
-	WipeTowerInfo* m_layer_info;
+	std::vector<WipeTowerInfo> m_plan; 	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
+	std::vector<WipeTowerInfo>::iterator m_layer_info;
 
 
 	// Returns gcode for wipe tower brim

From 3099c32d0805a6fff26929e9be3dcb27e50f9bfa Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 27 Feb 2018 08:56:11 +0100
Subject: [PATCH 015/118] GUI - reenabling rotation angle settings for the wipe
 tower (was disabled by the merge with master)

---
 xs/src/libslic3r/PrintConfig.cpp | 6 +++---
 xs/src/slic3r/GUI/Tab.cpp        | 3 ++-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index bc5d115c3..995dcee99 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1732,9 +1732,9 @@ PrintConfigDef::PrintConfigDef()
     def->default_value = new ConfigOptionFloat(15.);
 
     def = this->add("wipe_tower_rotation_angle", coFloat);
-    def->label = "Wipe tower rotation angle";
-    def->tooltip = "Wipe tower rotation angle with respect to x-axis ";
-    def->sidetext = "degrees";
+    def->label = _L("Wipe tower rotation angle");
+    def->tooltip = _L("Wipe tower rotation angle with respect to x-axis ");
+    def->sidetext = _L("degrees");
     def->cli = "wipe-tower-rotation-angle=f";
     def->default_value = new ConfigOptionFloat(0.);
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index b27e7b838..ebfa69eb5 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -446,6 +446,7 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_y");
 		optgroup->append_single_option_line("wipe_tower_width");
 		optgroup->append_single_option_line("wipe_tower_per_color_wipe");
+		optgroup->append_single_option_line("wipe_tower_rotation_angle");
 
 		optgroup = page->new_optgroup(_L("Advanced"));
 		optgroup->append_single_option_line("interface_shells");
@@ -762,7 +763,7 @@ void TabPrint::update()
 
 	bool have_wipe_tower = m_config->opt_bool("wipe_tower");
 	vec_enable.resize(0);
-	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe"};
+	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle"};
 	for (auto el : vec_enable)
 		get_field(el)->toggle(have_wipe_tower);
 

From a62ad3323f389e3ce93915b7fea92bd1eccf321e Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 28 Feb 2018 16:04:56 +0100
Subject: [PATCH 016/118] First naive implementation of wipe tower settings
 dialog

---
 xs/CMakeLists.txt                           |   5 +
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp |  65 ++---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 152 ++++++++++-
 xs/src/libslic3r/Print.cpp                  |   4 +-
 xs/src/libslic3r/PrintConfig.cpp            |   9 +-
 xs/src/libslic3r/PrintConfig.hpp            |   2 +
 xs/src/slic3r/GUI/BedShapeDialog.cpp        |   2 +-
 xs/src/slic3r/GUI/Preset.cpp                |   6 +-
 xs/src/slic3r/GUI/RammingChart.cpp          | 273 ++++++++++++++++++++
 xs/src/slic3r/GUI/RammingChart.hpp          | 129 +++++++++
 xs/src/slic3r/GUI/Tab.cpp                   |  24 ++
 xs/src/slic3r/GUI/Tab.hpp                   |   1 +
 xs/src/slic3r/GUI/WipeTowerDialog.cpp       | 264 +++++++++++++++++++
 xs/src/slic3r/GUI/WipeTowerDialog.hpp       | 115 +++++++++
 14 files changed, 996 insertions(+), 55 deletions(-)
 create mode 100644 xs/src/slic3r/GUI/RammingChart.cpp
 create mode 100644 xs/src/slic3r/GUI/RammingChart.hpp
 create mode 100644 xs/src/slic3r/GUI/WipeTowerDialog.cpp
 create mode 100644 xs/src/slic3r/GUI/WipeTowerDialog.hpp

diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 41bf9de26..4e217d148 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -195,6 +195,11 @@ add_library(libslic3r_gui STATIC
     ${LIBDIR}/slic3r/GUI/2DBed.hpp
     ${LIBDIR}/slic3r/GUI/wxExtensions.cpp
     ${LIBDIR}/slic3r/GUI/wxExtensions.hpp
+    ${LIBDIR}/slic3r/GUI/WipeTowerDialog.cpp
+    ${LIBDIR}/slic3r/GUI/WipeTowerDialog.hpp
+    ${LIBDIR}/slic3r/GUI/RammingChart.cpp
+    ${LIBDIR}/slic3r/GUI/RammingChart.hpp
+
 )
 
 add_library(admesh STATIC
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 2a78ed6f1..d7492fdbb 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -33,33 +33,6 @@ TODO LIST
 
 constexpr bool  peters_wipe_tower = false;	// sparse wipe tower inspired by Peter's post processor - not finished yet
 constexpr float min_layer_difference = 2*m_perimeter_width;
-constexpr float max_bridge_distance = 10.f; // in mm
-constexpr bool  improve_first_layer_adhesion = true;
-// experimental: ramming speed (mm^3/s) sampled in 0.25s intervals (one filament so far)
-const std::vector<float> ramming_speed = {7.6, 7.6, 7.6, 7.6, 9.0, 9.0, 9.0, 10.7, 10.7, 10.7};
-constexpr float ramming_step_multiplicator = 1.2f; // extra spacing may be needed for some materials
-constexpr float ramming_line_width_multiplicator = 1.5f;
-
-// experimental: time requested for cooling in seconds (common for all materials so far)
-constexpr float cooling_time = 14; // PVA: 20; SCAFF: 17; PLA+others: 14
-
-
-// volumes in mm^3 required for wipe: {{from 0 to ...},{from 1 to ...},{from 2 to ...},{from 3 to ...}}, usage [from][to]
-const std::vector<std::vector<float>> wipe_volumes = {{  0,120, 10, 50},
-													  { 20,  0, 30, 40},
-													  { 90, 20,  0, 85},
-													  {100,140, 30,  0}};
-
-/*const std::vector<std::vector<float>> wipe_volumes = {{0, 67, 67, 67},
-													  {67, 0, 67, 67},
-													  {67, 67, 0, 67},
-													  {67, 67, 67, 0}};
-*/
-/*const std::vector<std::vector<float>> wipe_volumes = {{0, 10, 10, 10},
-													  {10, 0, 10, 10},
-													  {10, 10, 0, 10},
-													  {10, 10, 10, 0}};
-*/
 
 namespace Slic3r
 {
@@ -614,7 +587,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 = m_par.wipe_volumes[b.old_tool][b.new_tool];
 				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;
@@ -804,20 +777,20 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	
 	writer.append("; CP TOOLCHANGE UNLOAD\n");
 	
-	const float line_width = m_line_width * ramming_line_width_multiplicator; // desired ramming line thickness
-	const float y_step = line_width * ramming_step_multiplicator * m_extra_spacing;			  // spacing between lines in mm
+	const float line_width = m_line_width * m_par.ramming_line_width_multiplicator[m_current_tool]; // desired ramming line thickness
+	const float y_step = line_width * m_par.ramming_step_multiplicator[m_current_tool] * m_extra_spacing;			  // spacing between lines in mm
 
 	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
 	float e_done = 0;									// measures E move done from each segment
-	
+
 	writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
 
-	while (i < ramming_speed.size())
+	while (i < m_par.ramming_speed[m_current_tool].size())
 	{
-		const float x = volume_to_length(ramming_speed[i] * 0.25f, line_width, m_layer_height);
-		const float e = ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move;
+		const float x = volume_to_length(m_par.ramming_speed[m_current_tool][i] * 0.25f, line_width, m_layer_height);
+		const float e = m_par.ramming_speed[m_current_tool][i] * 0.25f / Filament_Area; // transform volume per sec to E move;
 		const float dist = std::min(x - e_done, remaining);		  // distance to travel for either the next 0.25s, or to the next turnaround
 		const float actual_time = dist/x * 0.25;
 		writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
@@ -880,21 +853,21 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	const float start_x = writer.x();
 	const float turning_point = ( xr-start_x > start_x-xl ? xr : xl );
 	const float max_x_dist = 2*std::abs(start_x-turning_point);
-	const int N = 4 + (cooling_time-14)/3;
-	float time = cooling_time / N;
-	
+	const unsigned int N = 4 + std::max(0,(m_par.cooling_time[m_current_tool]-14)/3);
+	float time = m_par.cooling_time[m_current_tool] / N;
+
 	i = 0;
-	while (i<N) {		
+	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,10.f); // distance to travel
 		
 		if (speed * time < 10.f) { 	// this move is the last one at this speed
 			++i;
-			time = cooling_time / N;			
+			time = m_par.cooling_time[m_current_tool] / N;
 		}
 		else
 			time -= e_dist / speed; // subtract time this part will really take
-		
+
 		// 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);
@@ -1124,7 +1097,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 			    .extrude(box.ld);
 		}
 
-		if (m_is_first_layer && improve_first_layer_adhesion) {
+		if (m_is_first_layer && m_par.adhesion) {
 			// Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
 			box.expand(-m_perimeter_width/2.f);
 			unsigned nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
@@ -1150,7 +1123,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 
 				const float left = fill_box.lu.x+2*m_perimeter_width;
 				const float right = fill_box.ru.x - 2 * m_perimeter_width;
-				const int n = 1+(right-left)/max_bridge_distance;
+				const int n = 1+(right-left)/(m_par.bridging);
 				const float dx = (right-left)/n;
 				for (int i=1;i<=n;++i) {
 					float x=left+dx*i;
@@ -1207,12 +1180,12 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 	// this is an actual toolchange - let's calculate depth to reserve on the wipe tower
 	float depth = 0.f;			
 	float width = m_wipe_tower_width - 3*m_perimeter_width; 
-	float length_to_extrude = volume_to_length(0.25f * std::accumulate(ramming_speed.begin(), ramming_speed.end(), 0.f),
-										m_line_width * ramming_line_width_multiplicator,
+	float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_par.ramming_speed[old_tool].begin(), m_par.ramming_speed[old_tool].end(), 0.f),
+										m_line_width * m_par.ramming_line_width_multiplicator[old_tool],
 										layer_height_par);
-	depth = (int(length_to_extrude / width) + 1) * (m_line_width * ramming_line_width_multiplicator * ramming_step_multiplicator);
+	depth = (int(length_to_extrude / width) + 1) * (m_line_width * m_par.ramming_line_width_multiplicator[old_tool] * m_par.ramming_step_multiplicator[old_tool]);
 	length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
-	length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
+	length_to_extrude += volume_to_length(m_par.wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
 	length_to_extrude = std::max(length_to_extrude,0.f);
 	depth += (int(length_to_extrude / width) + 1) * m_line_width;
 	depth *= m_extra_spacing;	
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index f5fc2985e..43163a73a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -4,6 +4,7 @@
 #include <algorithm>
 #include <cmath>
 #include <string>
+#include <sstream>
 #include <utility>
 
 #include "WipeTower.hpp"
@@ -22,6 +23,11 @@ constexpr float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Ko
 constexpr float WT_EPSILON = 1e-3f;
 
 
+
+
+
+
+
 namespace Slic3r
 {
 
@@ -29,6 +35,141 @@ namespace PrusaMultiMaterial {
 	class Writer;
 };
 
+
+
+// Operator overload to output std::pairs
+template <typename T>
+std::ostream& operator<<(std::ostream& stream,const std::pair<T,T>& pair) {
+    return stream << pair.first << " " << pair.second;
+}
+
+// Operator overload to output elements of a vector to std::ofstream easily:
+template <typename T>
+std::ostream& operator<<(std::ostream& stream,const std::vector<T>& vect) {
+    for (const auto& element : vect)
+        stream << element << " ";
+    return stream;
+}
+
+// Operator overload to input elements of a vector from std::ifstream easily (reads until a fail)
+template <typename T>
+std::istream& operator>>(std::istream& stream, std::vector<T>& vect) {
+    vect.clear();
+    T value{};
+    bool we_read_something = false;
+    while (stream >> value) {
+        vect.push_back(value);
+        we_read_something = true;
+    }
+    if (!stream.eof() && we_read_something) { // if this is not eof, we might be at separator - let's get rid of it
+        stream.clear();     // if we failed on very first line or reached eof, return stream in !good() state
+        stream.get();       // get() whatever we are stuck at
+    }
+    return stream;
+}
+
+
+// This struct is used to store parameters and to pass it to wipe tower generator
+struct WipeTowerParameters {
+    WipeTowerParameters() {  }           // create new empty object
+    WipeTowerParameters(const std::string& init_data) { // create object and initialize from std::string
+        std::istringstream in(init_data);               // validation of input is left to the caller
+        in >> bridging >> adhesion >> sampling;
+        for (std::vector<float> vect{} ; in >> vect ;) {  // until we get to fail state ("**")...
+            if (vect.size()>=3) {
+                cooling_time.push_back(vect[0]);
+                ramming_line_width_multiplicator.push_back(vect[1]);
+                ramming_step_multiplicator.push_back(vect[2]);
+                vect.erase(vect.begin(),vect.begin()+3);
+            }
+            else vect.clear(); // something's not right, we will restore defaults anyway
+            ramming_speed.push_back(vect);
+
+            if (in.good()) {
+                in >> vect;
+                std::vector<std::pair<float,float>> pairs;
+                for (unsigned int i=0;i<vect.size();++i)
+                    if (i%2==1)
+                        pairs.push_back(std::make_pair(vect[i-1],vect[i]));
+                ramming_buttons.push_back(pairs);
+            }
+        }
+        in.clear();
+        in.get();
+
+        for (std::vector<float> vect{} ; in >> vect ;) {  // let's keep reading
+            wipe_volumes.push_back(vect);
+        }
+        in.clear();
+        in.get();
+
+        std::vector<int> vect{};
+        in >> vect;
+        for (unsigned int i=0;i<vect.size();++i)
+            if (i%2==1)
+                filament_wipe_volumes.push_back(std::make_pair(vect[i-1],vect[i]));
+    }
+
+    std::string to_string() {
+        std::ostringstream out;
+        out << bridging << " " << int(adhesion) << " " << sampling << "\n";
+        for (unsigned extruder=0;extruder<cooling_time.size();++extruder) {
+            out << "\n" << cooling_time[extruder] << " "  << ramming_line_width_multiplicator[extruder] << " " 
+                << ramming_step_multiplicator[extruder] << " " << ramming_speed[extruder]  << "*"
+                << ramming_buttons[extruder] << "*";
+        }
+        out << "*\n";
+        for (auto& radek : wipe_volumes)
+            out << "\n" << radek << "*";
+        out << "*\n";
+        out << filament_wipe_volumes << "*";
+        return out.str();
+    }
+
+    bool validate() const {     // basic check for validity to distinguish most dramatic failures
+        const unsigned int num = cooling_time.size();
+        if ( num < 1 || ramming_line_width_multiplicator.size()!=num || ramming_step_multiplicator.size()!=num ||
+             ramming_buttons.size()!=num || wipe_volumes.size()!=num ||
+             filament_wipe_volumes.size()!=num)
+            return false;
+        for (const auto& row : wipe_volumes)
+            if (row.size()!=num)
+                return false;
+        return true;
+    }
+    void set_defaults() {
+        bridging = 10;
+        adhesion = true;
+        sampling = 0.25f;
+        cooling_time = {15,15,15,15};
+        ramming_line_width_multiplicator = {1.5f, 1.5f, 1.5f, 1.5f};
+        ramming_step_multiplicator = {1.1f, 1.1f, 1.1f, 1.1f};
+        ramming_speed.clear();
+        ramming_buttons.clear();
+        for (unsigned int i=0;i<4;++i) {
+            ramming_speed.push_back(std::vector<float>{7.6, 7.6, 7.6, 7.6, 9.0, 9.0, 9.0, 10.7, 10.7, 10.7});
+            ramming_buttons.push_back(std::vector<std::pair<float,float>>{{0.05, 6.6},{0.45, 6.8},{0.95, 7.8},{1.45, 8.3},{1.95, 9.7},{2.45,10},{2.95, 7.6},{3.45, 7.6},{3.95, 7.6},{4.45, 7.6},{4.95, 7.6}});
+        }
+        wipe_volumes = {{  0, 60, 60, 60},
+                        { 60,  0, 60, 60},
+                        { 60, 60,  0, 60},
+                        { 60, 60, 60,  0}};
+        filament_wipe_volumes = {{30,30},{30,30},{30,30},{30,30}};
+    }
+
+    int bridging = 0.f;
+    bool adhesion  = false;
+    float sampling = 0.25f; // this does not quite work yet, keep it fixed to 0.25f
+    std::vector<int> cooling_time;
+    std::vector<float> ramming_line_width_multiplicator;
+    std::vector<float> ramming_step_multiplicator;
+    std::vector<std::vector<float>> ramming_speed;
+    std::vector<std::vector<std::pair<float,float>>> ramming_buttons;
+    std::vector<std::vector<float>> wipe_volumes;
+    std::vector<std::pair<int,int>> filament_wipe_volumes;
+};
+
+
 class WipeTowerPrusaMM : public WipeTower
 {
 public:
@@ -53,15 +194,16 @@ public:
 	// y			-- y coordinates of wipe tower in mm ( left bottom corner )
 	// width		-- width of wipe tower in mm ( default 60 mm - leave as it is )
 	// wipe_area	-- space available for one toolchange in mm
-	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, unsigned int initial_tool) :
+	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, unsigned int initial_tool,std::string& parameters) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
 		m_wipe_tower_rotation_angle(rotation_angle),
 		m_y_shift(0.f),
 		m_z_pos(0.f),
 		m_is_first_layer(false),
-		m_is_last_layer(false),		
-		m_current_tool(initial_tool)
+		m_is_last_layer(false),
+		m_current_tool(initial_tool),
+        m_par(parameters)
  	{
 		for (size_t i = 0; i < 4; ++ i) {
 			// Extruder specific parameters.
@@ -206,6 +348,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;
+    WipeTowerParameters m_par;
 
 	float m_depth_traversed = 0.f; // Current y position at the wipe tower.
 	// How much to wipe the 1st extruder over the wipe tower at the 1st layer
@@ -310,6 +453,9 @@ private:
 		float wipe_volume);	
 };
 
+
+
+
 }; // namespace Slic3r
 
 #endif /* WipeTowerPrusaMM_hpp_ */
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 1c63dbf60..fe98612cf 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -187,6 +187,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "spiral_vase"
             || opt_key == "temperature"
             || opt_key == "wipe_tower"
+            || opt_key == "wipe_tower_advanced"
             || opt_key == "wipe_tower_x"
             || opt_key == "wipe_tower_y"
             || opt_key == "wipe_tower_width"
@@ -1021,7 +1022,8 @@ void Print::_make_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_per_color_wipe.value),
-        float(this->config.wipe_tower_rotation_angle.value), m_tool_ordering.first_extruder());
+        float(this->config.wipe_tower_rotation_angle.value), m_tool_ordering.first_extruder(),
+        this->config.wipe_tower_advanced.value);
     
     //wipe_tower.set_retract();
     //wipe_tower.set_zhop();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 995dcee99..7a48b3a32 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1701,6 +1701,13 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "wipe-tower!";
     def->default_value = new ConfigOptionBool(false);
 
+    def = this->add("wipe_tower_advanced", coString);
+    def->label = _L("Advanced string");
+    def->tooltip = _L("Advanced tooltip ");
+    def->sidetext = _L("advanced sidetext");
+    def->cli = "wipe-tower-advanced=s";
+    def->default_value = new ConfigOptionString("");
+
     def = this->add("wipe_tower_x", coFloat);
     def->label = _L("Position X");
     def->tooltip = _L("X coordinate of the left front corner of a wipe tower");
@@ -1723,7 +1730,7 @@ PrintConfigDef::PrintConfigDef()
     def->default_value = new ConfigOptionFloat(60.);
 
     def = this->add("wipe_tower_per_color_wipe", coFloat);
-    def->label = _L("Per color change depth");
+    def->label = "(Unused and will be likely removed)";//_L("Per color change depth");
     def->tooltip = _L("Depth of a wipe color per color change. For N colors, there will be "
                    "maximum (N-1) tool switches performed, therefore the total depth "
                    "of the wipe tower will be (N-1) times this value.");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 7c2d40458..d55aafed6 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -605,6 +605,7 @@ public:
     ConfigOptionInt                 threads;
     ConfigOptionBools               wipe;
     ConfigOptionBool                wipe_tower;
+    ConfigOptionString              wipe_tower_advanced;
     ConfigOptionFloat               wipe_tower_x;
     ConfigOptionFloat               wipe_tower_y;
     ConfigOptionFloat               wipe_tower_width;
@@ -670,6 +671,7 @@ protected:
         OPT_PTR(threads);
         OPT_PTR(wipe);
         OPT_PTR(wipe_tower);
+        OPT_PTR(wipe_tower_advanced);
         OPT_PTR(wipe_tower_x);
         OPT_PTR(wipe_tower_y);
         OPT_PTR(wipe_tower_width);
diff --git a/xs/src/slic3r/GUI/BedShapeDialog.cpp b/xs/src/slic3r/GUI/BedShapeDialog.cpp
index 5ee0c1f8b..8e7f57241 100644
--- a/xs/src/slic3r/GUI/BedShapeDialog.cpp
+++ b/xs/src/slic3r/GUI/BedShapeDialog.cpp
@@ -34,7 +34,7 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
 
 void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
 {
-//	on_change(nullptr);
+//  on_change(nullptr);
 
 	auto box = new wxStaticBox(this, wxID_ANY, _L("Shape"));
 	auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 4d55f347a..be31c1bd1 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -198,9 +198,9 @@ const std::vector<std::string>& Preset::print_options()
         "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", 
         "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_per_color_wipe", "wipe_tower_rotation_angle",
-        "compatible_printers", "compatible_printers_condition"
+        "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower_advanced", "wipe_tower", "wipe_tower_x",
+        "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "compatible_printers",
+        "compatible_printers_condition"
 
     };
     return s_opts;
diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
new file mode 100644
index 000000000..3b5f4e41e
--- /dev/null
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -0,0 +1,273 @@
+#include <algorithm>
+#include "RammingChart.hpp"
+
+
+wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
+
+
+void Chart::draw(wxDC& dc) {
+    dc.SetPen(*wxBLACK_PEN);
+    dc.SetBrush(*wxWHITE_BRUSH);
+    dc.DrawRectangle(m_rect);
+    
+    if (visible_area->m_width < 0.499) {
+        dc.DrawText("NO RAMMING AT ALL",wxPoint(m_rect.GetLeft()+m_rect.GetWidth()/2-50,m_rect.GetBottom()-m_rect.GetHeight()/2));
+        return;
+    }
+    
+    
+    if (!m_line_to_draw->empty()) {
+        for (uint i=0;i<m_line_to_draw->size()-2;++i) {
+            int color = 510*((m_rect.GetBottom()-(*m_line_to_draw)[i])/double(m_rect.GetHeight()));
+            dc.SetPen( wxPen( wxColor(std::min(255,color),255-std::max(color-255,0),0), 1 ) );
+            dc.DrawLine(m_rect.GetLeft()+1+i,(*m_line_to_draw)[i],m_rect.GetLeft()+1+i,m_rect.GetBottom());        
+        }
+        dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
+        for (uint i=0;i<m_line_to_draw->size()-2;++i) {
+            if (splines)
+                dc.DrawLine(m_rect.GetLeft()+i,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i+1]);        
+            else {
+                dc.DrawLine(m_rect.GetLeft()+i,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i]);        
+                dc.DrawLine(m_rect.GetLeft()+i+1,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i+1]);        
+            }                
+        }
+    }
+    
+    // draw draggable buttons
+    dc.SetBrush(*wxBLUE_BRUSH);
+    dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
+    for (auto& button : m_buttons[m_current_extruder])
+        //dc.DrawRectangle(math_to_screen(button.get_pos())-wxPoint(side/2.,side/2.), wxSize(side,side));
+        dc.DrawCircle(math_to_screen(button.get_pos()),side/2.);
+        //dc.DrawRectangle(math_to_screen(button.get_pos()-wxPoint2DDouble(0.125,0))-wxPoint(0,5),wxSize(50,10));
+
+    // draw x-axis:
+    float last_mark = -10000;
+    for (float math_x=int(visible_area->m_x*10)/10 ; math_x <= (visible_area->m_x+visible_area->m_width) ; math_x+=0.1) {
+        int x = math_to_screen(wxPoint2DDouble(math_x,visible_area->m_y)).x;
+        int y = m_rect.GetBottom(); 
+        if (x-last_mark < 50) continue;
+        dc.DrawLine(x,y+3,x,y-3);
+        dc.DrawText(wxString().Format(wxT("%.1f"), math_x),wxPoint(x-10,y+7));
+        last_mark = x;
+    }
+    
+    // draw y-axis:
+    last_mark=10000;
+    for (int math_y=visible_area->m_y ; math_y <= (visible_area->m_y+visible_area->m_height) ; math_y+=1) {
+        int y = math_to_screen(wxPoint2DDouble(visible_area->m_x,math_y)).y;
+        int x = m_rect.GetLeft();
+        if (last_mark-y < 50) continue;    
+        dc.DrawLine(x-3,y,x+3,y);
+        dc.DrawText(wxString()<<math_y,wxPoint(x-25,y-7));
+        last_mark = y;
+    }
+    
+    
+    
+}
+
+void Chart::mouse_right_button_clicked(wxMouseEvent& event) {
+    if (!manual_points_manipulation)
+        return;
+    wxPoint point = event.GetPosition();
+    int button_index = which_button_is_clicked(point);
+    if (button_index != -1 && m_buttons[m_current_extruder].size()>2) {
+        m_buttons[m_current_extruder].erase(m_buttons[m_current_extruder].begin()+button_index);
+        recalculate_line();
+    }
+}
+    
+
+
+void Chart::mouse_clicked(wxMouseEvent& event) {
+    wxPoint point = event.GetPosition();
+    int button_index = which_button_is_clicked(point);
+    if ( button_index != -1) {
+        m_dragged = &m_buttons[m_current_extruder][button_index];
+        m_previous_mouse = point;            
+    }
+}
+    
+    
+    
+void Chart::mouse_moved(wxMouseEvent& event) {
+    if (!event.Dragging() || !m_dragged) return;
+    wxPoint pos = event.GetPosition();    
+    wxRect rect = m_rect;
+    rect.Deflate(side/2.);
+    if (!(rect.Contains(pos))) {  // the mouse left chart area
+        mouse_left_window(event);
+        return;
+    }    
+    int delta_x = pos.x - m_previous_mouse.x;
+    int delta_y = pos.y - m_previous_mouse.y;
+    m_dragged->move(fixed_x?0:double(delta_x)/m_rect.GetWidth() * visible_area->m_width,-double(delta_y)/m_rect.GetHeight() * visible_area->m_height); 
+    m_previous_mouse = pos;
+    recalculate_line();
+}
+
+
+
+void Chart::mouse_double_clicked(wxMouseEvent& event) {
+    if (!manual_points_manipulation)
+        return;
+    wxPoint point = event.GetPosition();
+    if (!m_rect.Contains(point))     // the click is outside the chart
+        return;
+    m_buttons[m_current_extruder].push_back(screen_to_math(point));
+    std::sort(m_buttons[m_current_extruder].begin(),m_buttons[m_current_extruder].end());
+    recalculate_line();
+    return;
+}
+
+
+
+
+void Chart::recalculate_line() {
+    std::vector<wxPoint> points;
+    for (auto& but : m_buttons[m_current_extruder]) {
+        points.push_back(wxPoint(math_to_screen(but.get_pos())));
+        if (points.size()>1 && points.back().x==points[points.size()-2].x) points.pop_back();
+        if (points.size()>1 && points.back().x > m_rect.GetRight()) {
+            points.pop_back();
+            break;
+        }
+    }
+    std::sort(points.begin(),points.end(),[](wxPoint& a,wxPoint& b) { return a.x < b.x; });
+    
+    m_line_to_draw->clear();
+    m_total_volume = 0.f;
+   
+   
+    // Cubic spline interpolation: see https://en.wikiversity.org/wiki/Cubic_Spline_Interpolation#Methods
+    const bool boundary_first_derivative = true; // true - first derivative is 0 at the leftmost and rightmost point
+                                                 // false - second ---- || -------
+    const int N = points.size()-1; // last point can be accessed as N, we have N+1 total points
+    std::vector<float> diag(N+1);
+    std::vector<float> mu(N+1);
+    std::vector<float> lambda(N+1);
+    std::vector<float> h(N+1);
+    std::vector<float> rhs(N+1);
+    
+    // let's fill in inner equations
+    for (int i=1;i<=N;++i) h[i] = points[i].x-points[i-1].x;
+    std::fill(diag.begin(),diag.end(),2.f);
+    for (int i=1;i<=N-1;++i) {
+        mu[i] = h[i]/(h[i]+h[i+1]);
+        lambda[i] = 1.f - mu[i];
+        rhs[i] = 6 * ( float(points[i+1].y-points[i].y  )/(h[i+1]*(points[i+1].x-points[i-1].x)) -  
+                       float(points[i].y  -points[i-1].y)/(h[i]  *(points[i+1].x-points[i-1].x))   );                       
+    }
+    
+    // now fill in the first and last equations, according to boundary conditions:
+    if (boundary_first_derivative) {
+        const float endpoints_derivative = 0;
+        lambda[0] = 1;
+        mu[N]     = 1;
+        rhs[0] = (6.f/h[1]) * (float(points[0].y-points[1].y)/(points[0].x-points[1].x) - endpoints_derivative);
+        rhs[N] = (6.f/h[N]) * (endpoints_derivative - float(points[N-1].y-points[N].y)/(points[N-1].x-points[N].x));        
+    }
+    else {
+        lambda[0] = 0;
+        mu[N]     = 0;
+        rhs[0]    = 0;
+        rhs[N]    = 0;
+    }
+        
+    // the trilinear system is ready to be solved:
+    for (int i=1;i<=N;++i) {
+        float multiple = mu[i]/diag[i-1];    // let's subtract proper multiple of above equation
+        diag[i]-= multiple * lambda[i-1];
+        rhs[i] -= multiple * rhs[i-1];        
+    }
+    // now the back substitution (vector mu contains invalid values from now on):
+    rhs[N] = rhs[N]/diag[N];
+    for (int i=N-1;i>=0;--i)
+        rhs[i] = (rhs[i]-lambda[i]*rhs[i+1])/diag[i];
+    
+    
+    
+    
+    unsigned int i=1;
+    float y=0.f;
+    for (int x=m_rect.GetLeft(); x<=m_rect.GetRight() ; ++x) {        
+        if (splines) {
+            if (i<points.size()-1 && points[i].x < x ) {
+                ++i; 
+            }
+            if (points[0].x > x)
+                y = points[0].y;
+            else
+                if (points[N].x < x)
+                    y = points[N].y;
+                else 
+                    y = (rhs[i-1]*pow(points[i].x-x,3)+rhs[i]*pow(x-points[i-1].x,3)) / (6*h[i]) +
+                        (points[i-1].y-rhs[i-1]*h[i]*h[i]/6.f) * (points[i].x-x)/h[i] +
+                        (points[i].y  -rhs[i]  *h[i]*h[i]/6.f) * (x-points[i-1].x)/h[i];
+            m_line_to_draw->push_back(y);
+        }
+        else {            
+            float x_math = screen_to_math(wxPoint(x,0)).m_x;
+            if (i+2<=points.size() && m_buttons[m_current_extruder][i+1].get_pos().m_x-0.125 < x_math)
+                ++i;
+            m_line_to_draw->push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[m_current_extruder][i].get_pos().m_y)).y);
+        }
+            
+                    
+        m_line_to_draw->back() = std::max(m_line_to_draw->back(), m_rect.GetTop()-1);
+        m_line_to_draw->back() = std::min(m_line_to_draw->back(), m_rect.GetBottom()-1);
+        m_total_volume += (m_rect.GetBottom() - m_line_to_draw->back()) * (visible_area->m_width / m_rect.GetWidth()) * (visible_area->m_height / m_rect.GetHeight());
+    }
+    
+    wxPostEvent(this->GetParent(), wxCommandEvent(EVT_WIPE_TOWER_CHART_CHANGED));
+    Refresh();
+}
+
+
+
+std::vector<std::vector<float>> Chart::get_ramming_speeds(float sampling) const {
+    std::vector<std::vector<float>> speeds_out;
+    for (unsigned int extruder_id = 0;extruder_id<m_buttons.size();++extruder_id) { // repeat for each extruder
+        std::vector<float> this_extruder;
+        const int number_of_samples = std::round( visible_areas[extruder_id].m_width / sampling);
+        if (number_of_samples>0) {
+            const int dx = (m_lines_to_draw[extruder_id].size()-1) / number_of_samples;
+            for (int j=0;j<number_of_samples;++j) {
+                float left =  screen_to_math(wxPoint(0,m_lines_to_draw[extruder_id][j*dx])).m_y;
+                float right = screen_to_math(wxPoint(0,m_lines_to_draw[extruder_id][(j+1)*dx])).m_y;
+                this_extruder.push_back((left+right)/2.f);            
+            }
+        }
+        /*else
+            this_extruder.push_back(0.f); // so it does not stay empty*/
+        speeds_out.push_back(std::move(this_extruder));            
+    }
+    
+    return speeds_out;
+}
+
+
+std::vector<std::vector<std::pair<float,float>>> Chart::get_buttons() const {
+    std::vector<std::vector<std::pair<float, float>>> buttons_out;
+    for (const auto& ext : m_buttons) {
+        std::vector<std::pair<float,float>> this_extruder;
+        for (const auto& button : ext)
+            this_extruder.push_back(std::make_pair(button.get_pos().m_x,button.get_pos().m_y));            
+        buttons_out.push_back(std::move(this_extruder));            
+    }
+    return buttons_out;
+}
+    
+    
+    
+
+BEGIN_EVENT_TABLE(Chart, wxWindow)
+EVT_MOTION(Chart::mouse_moved)
+EVT_LEFT_DOWN(Chart::mouse_clicked)
+EVT_LEFT_UP(Chart::mouse_released)
+EVT_LEFT_DCLICK(Chart::mouse_double_clicked)
+EVT_RIGHT_DOWN(Chart::mouse_right_button_clicked)
+EVT_LEAVE_WINDOW(Chart::mouse_left_window)
+EVT_PAINT(Chart::paint_event)
+END_EVENT_TABLE()
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
new file mode 100644
index 000000000..5443e957a
--- /dev/null
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -0,0 +1,129 @@
+#ifndef RAMMING_CHART_H_
+#define RAMMING_CHART_H_
+
+#include <vector>
+#include <wx/wxprec.h>
+#ifndef WX_PRECOMP
+    #include <wx/wx.h>
+#endif
+
+wxDECLARE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
+
+
+class Chart : public wxWindow {
+        
+public:
+    Chart(wxWindow* parent, wxRect rect,const std::vector<std::vector<std::pair<float,float>>>& initial_buttons,std::vector<std::vector<float>> ramming_speed, float sampling) :
+        wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize())
+    {
+        m_rect=wxRect(wxPoint(30,0),rect.GetSize()-wxSize(30,30));
+        for (int i=0;i<4;++i) {
+            visible_areas.push_back(wxRect2DDouble(0.0, 0.0, sampling*ramming_speed[i].size(), 20.));
+            m_buttons.push_back(std::vector<ButtonToDrag>());
+            m_lines_to_draw.push_back(std::vector<int>());
+            if (initial_buttons.size()>0)
+                for (const auto& pair : initial_buttons[i])
+                    m_buttons.back().push_back(wxPoint2DDouble(pair.first,pair.second));
+            set_extruder(i); // to calculate all interpolating splines
+        }
+        set_extruder(0);
+    }
+    void set_extruder(unsigned ext) {
+        if (ext>=4) return;
+        m_current_extruder = ext;
+        visible_area = &(visible_areas[ext]);
+        m_line_to_draw = &(m_lines_to_draw[ext]);
+        recalculate_line();
+    }
+    void set_xy_range(float x,float y) {
+        x = int(x/0.5) * 0.5;
+        if (x>=0) visible_area->SetRight(x);
+        if (y>=0) visible_area->SetBottom(y);
+        recalculate_line();
+    }
+    float get_volume() const { return m_total_volume; }
+    float get_time()   const { return visible_area->m_width; }
+    std::vector<std::vector<float>> get_ramming_speeds(float sampling) const; //returns sampled ramming speed for all extruders
+    std::vector<std::vector<std::pair<float,float>>> get_buttons() const; // returns buttons position for all extruders
+   
+    
+    void draw(wxDC& dc);
+    
+    void mouse_clicked(wxMouseEvent& event);
+    void mouse_right_button_clicked(wxMouseEvent& event);
+    void mouse_moved(wxMouseEvent& event);
+    void mouse_double_clicked(wxMouseEvent& event);
+    void mouse_left_window(wxMouseEvent&) { m_dragged = nullptr; }        
+    void mouse_released(wxMouseEvent&)    { m_dragged = nullptr; }
+    void paint_event(wxPaintEvent&) { wxPaintDC dc(this); draw(dc); }
+    DECLARE_EVENT_TABLE()
+    
+
+        
+private:
+    static const bool fixed_x = true;
+    static const bool splines = true;
+    static const bool manual_points_manipulation = false;
+    static const int side = 10; // side of draggable button
+
+    class ButtonToDrag {
+    public:
+        bool operator<(ButtonToDrag& a) { return m_pos.m_x < a.m_pos.m_x; }
+        ButtonToDrag(wxPoint2DDouble pos) : m_pos{pos} {};
+        wxPoint2DDouble get_pos() const { return m_pos; }            
+        void move(double x,double y) { m_pos.m_x+=x; m_pos.m_y+=y; }
+    private:
+        wxPoint2DDouble m_pos;              // position in math coordinates                       
+    };
+    
+    
+    
+    wxPoint math_to_screen(const wxPoint2DDouble& math) const {
+        wxPoint screen;
+        screen.x = (math.m_x-visible_area->m_x) * (m_rect.GetWidth()  / visible_area->m_width  );
+        screen.y = (math.m_y-visible_area->m_y) * (m_rect.GetHeight() / visible_area->m_height );
+        screen.y *= -1;
+        screen += m_rect.GetLeftBottom();            
+        return screen;
+    }
+    wxPoint2DDouble screen_to_math(const wxPoint& screen) const {
+        wxPoint2DDouble math = screen;
+        math -= m_rect.GetLeftBottom();
+        math.m_y *= -1;
+        math.m_x *= visible_area->m_width   / m_rect.GetWidth();    // scales to [0;1]x[0,1]
+        math.m_y *= visible_area->m_height / m_rect.GetHeight();
+        return (math+visible_area->GetLeftTop());
+    }
+        
+    int which_button_is_clicked(const wxPoint& point) const {
+        if (!m_rect.Contains(point))
+            return -1;
+        for (uint i=0;i<m_buttons[m_current_extruder].size();++i) {
+            wxRect rect(math_to_screen(m_buttons[m_current_extruder][i].get_pos())-wxPoint(side/2.,side/2.),wxSize(side,side)); // bounding rectangle of this button
+            if ( rect.Contains(point) )
+                return i;
+        }
+        return (-1);
+    }
+        
+        
+    void recalculate_line();
+    void recalculate_volume();
+     
+    
+    unsigned m_current_extruder = 0;
+    wxRect m_rect;                  // rectangle on screen the chart is mapped into (screen coordinates)
+    wxPoint m_previous_mouse;        
+    std::vector< std::vector<ButtonToDrag> > m_buttons;
+    std::vector< std::vector<int> > m_lines_to_draw;
+    std::vector< wxRect2DDouble > visible_areas;
+    wxRect2DDouble* visible_area = nullptr;
+    std::vector<int>* m_line_to_draw = nullptr;
+    ButtonToDrag* m_dragged = nullptr;
+    float m_total_volume = 0.f;
+    
+    
+};
+
+
+#endif // RAMMING_CHART_H_
\ No newline at end of file
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index ebfa69eb5..7e1ad5dc5 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -3,6 +3,7 @@
 #include "PresetBundle.hpp"
 #include "PresetHints.hpp"
 #include "../../libslic3r/Utils.hpp"
+#include "WipeTowerDialog.hpp"
 
 #include <wx/app.h>
 #include <wx/button.h>
@@ -440,6 +441,7 @@ void TabPrint::build()
 		optgroup->append_single_option_line("ooze_prevention");
 		optgroup->append_single_option_line("standby_temperature_delta");
 
+    if (true) {
 		optgroup = page->new_optgroup(_L("Wipe tower"));
 		optgroup->append_single_option_line("wipe_tower");
 		optgroup->append_single_option_line("wipe_tower_x");
@@ -447,6 +449,27 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_width");
 		optgroup->append_single_option_line("wipe_tower_per_color_wipe");
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
+        Line line{ _L("Advanced"), "" };
+		line.widget = [this](wxWindow* parent){
+			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _L("Advanced settings")+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+			auto sizer = new wxBoxSizer(wxHORIZONTAL);
+			sizer->Add(m_wipe_tower_btn);
+			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
+			{
+                std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
+                std::cout << "dialog init: " << init_data << std::endl;
+				WipeTowerDialog dlg(this,init_data); // dlg lives on stack, no need to call Destroy
+
+				if (dlg.ShowModal() == wxID_OK) {
+                    load_key_value("wipe_tower_advanced", dlg.GetValue());
+                    std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
+                }
+			}));
+			return sizer;
+		};
+		optgroup->append_line(line);
+    }
+
 
 		optgroup = page->new_optgroup(_L("Advanced"));
 		optgroup->append_single_option_line("interface_shells");
@@ -766,6 +789,7 @@ void TabPrint::update()
 	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle"};
 	for (auto el : vec_enable)
 		get_field(el)->toggle(have_wipe_tower);
+    m_wipe_tower_btn->Enable(have_wipe_tower);
 
 	m_recommended_thin_wall_thickness_description_line->SetText(
 		PresetHints::recommended_thin_wall_thickness(*m_preset_bundle));
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 4f3a15736..b6d273e38 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -90,6 +90,7 @@ protected:
 	wxImageList*		m_icons;
 	wxCheckBox*			m_compatible_printers_checkbox;
 	wxButton*			m_compatible_printers_btn;
+    wxButton*           m_wipe_tower_btn;
 
 	int					m_icon_count;
 	std::map<std::string, size_t>	m_icon_index;		// Map from an icon file name to its index in $self->{icons}.
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
new file mode 100644
index 000000000..5b188e6d1
--- /dev/null
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -0,0 +1,264 @@
+#include "WipeTowerDialog.hpp"
+
+// Human-readable output of Parameters structure
+std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par) {
+    str << "bridging: " << par.bridging << "\n";
+    str << "adhesion: " << par.adhesion << "\n";
+    str << "sampling: " << par.sampling << "\n"; 
+   
+    str << "cooling times: ";
+    for (const auto& a : par.cooling_time) str << a << " ";
+    
+    str << "line widths: ";
+    for (const auto& a : par.ramming_line_width_multiplicator) str << a << " ";
+    
+    str << "line spacing: ";
+    for (const auto& a : par.ramming_step_multiplicator) str << a << " ";
+    
+    str<<"\n\nramming_speeds:\n";
+    for (const auto& a : par.ramming_speed) {
+        for (const auto& b : a)
+            str << b << " ";
+        str<<"\n";
+    }
+    str<<"\n\nramming_buttons:\n";
+    for (const auto& a : par.ramming_buttons) {
+        for (const auto& b : a) {
+            Slic3r::operator <<(str,b); // temporary hack (this << is in the namespace Slic3r)
+            str << " | ";               // the function will be deleted after everything is debugged, anyway
+        }
+        str<<"\n";
+    }
+    str<<"\n\nwipe volumes:\n";
+    for (const auto& a : par.wipe_volumes) {
+        for (const auto& b : a)
+            str << b << " ";
+        str<<"\n";
+    }
+    str<<"\n\nfilament wipe volumes:\n";
+    for (const auto& a : par.filament_wipe_volumes) {
+        Slic3r::operator <<(str,a);
+        str << " ";
+    }
+    str<<"\n";
+        
+    return str;
+}
+    
+
+
+
+RammingPanel::RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+{
+    new wxStaticText(this,wxID_ANY,wxString("Total ramming time (s):"),     wxPoint(500,105),      wxSize(200,25),wxALIGN_LEFT);
+    m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,       wxPoint(700,100),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0.,5.0,3.,0.5);        
+    new wxStaticText(this,wxID_ANY,wxString("Total rammed volume (mm3):"),  wxPoint(500,135),      wxSize(200,25),wxALIGN_LEFT);
+    m_widget_volume = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,           wxPoint(700,130),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,10000,0);        
+    new wxStaticText(this,wxID_ANY,wxString("Ramming line width (%):"),     wxPoint(500,205),      wxSize(200,25),wxALIGN_LEFT);
+    m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,       wxPoint(700,200),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        
+    new wxStaticText(this,wxID_ANY,wxString("Ramming line spacing (%):"),   wxPoint(500,235),      wxSize(200,25),wxALIGN_LEFT);
+    m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,     wxPoint(700,230),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        
+    new wxStaticText(this,wxID_ANY,wxString("Extruder #:"),                 wxPoint(500,12),       wxSize(200,25),wxALIGN_LEFT);
+    
+    wxArrayString choices;
+    for (unsigned int i=0;i<p.ramming_line_width_multiplicator.size();++i) {            // for all extruders
+        choices.Add(wxString("")<<i+1);
+        m_ramming_line_width_multiplicators.push_back(p.ramming_line_width_multiplicator[i]*100);
+        m_ramming_step_multiplicators.push_back(p.ramming_step_multiplicator[i]*100);
+    }
+    m_widget_extruder = new wxChoice(this,wxID_ANY,wxPoint(580,5),wxSize(50,27),choices);
+    
+    m_chart = new Chart(this,wxRect(10,10,480,360),p.ramming_buttons,p.ramming_speed,p.sampling);
+    
+    m_chart->set_extruder(0);
+    m_widget_time->SetValue(m_chart->get_time());
+    m_widget_time->SetDigits(2);
+    m_widget_volume->SetValue(m_chart->get_volume());
+    m_widget_volume->Disable();
+    m_widget_extruder->SetSelection(0);
+    extruder_selection_changed(); // tell everyone to redraw
+    
+    m_widget_ramming_step_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
+    m_widget_ramming_line_width_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
+    m_widget_extruder->Bind(wxEVT_CHOICE,[this](wxCommandEvent&) { extruder_selection_changed(); });
+    m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(m_widget_time->GetValue(),-1);});
+    m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){});      // do nothing - prevents the user to change the value
+    m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){});    // do nothing - prevents the user to change the value   
+    Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} );        
+}
+
+void RammingPanel::fill_parameters(Slic3r::WipeTowerParameters& p)
+{
+    if (!m_chart) return;
+    p.ramming_buttons = m_chart->get_buttons();
+    p.ramming_speed   = m_chart->get_ramming_speeds(p.sampling);
+    for (unsigned int i=0;i<m_ramming_line_width_multiplicators.size();++i) {  // we assume m_ramming_line_width_multiplicators.size() == m_ramming_step_multiplicators.size()         
+        p.ramming_line_width_multiplicator.push_back(m_ramming_line_width_multiplicators[i]/100.f);
+        p.ramming_step_multiplicator.push_back(m_ramming_step_multiplicators[i]/100.f);
+    }
+}
+    
+void RammingPanel::extruder_selection_changed() {
+    m_current_extruder = m_widget_extruder->GetSelection();
+    m_chart->set_extruder(m_current_extruder);  // tell our chart to redraw
+    m_widget_ramming_line_width_multiplicator  ->SetValue(m_ramming_line_width_multiplicators[m_current_extruder]);
+    m_widget_ramming_step_multiplicator->SetValue(m_ramming_step_multiplicators[m_current_extruder]);        
+}
+
+void RammingPanel::line_parameters_changed() {
+    m_ramming_line_width_multiplicators[m_current_extruder]=m_widget_ramming_line_width_multiplicator->GetValue();
+    m_ramming_step_multiplicators[m_current_extruder]=m_widget_ramming_step_multiplicator->GetValue();
+}
+
+
+
+
+
+
+CoolingPanel::CoolingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+{
+    new wxStaticText(this,wxID_ANY,wxString("Time (in seconds) reserved for cooling after unload:"),wxPoint(220,50) ,wxSize(400,25),wxALIGN_LEFT);
+    for (int i=0;i<4;++i) {
+        new wxStaticText(this,wxID_ANY,wxString("Filament #")<<i+1<<": ",wxPoint(300,105+30*i) ,wxSize(150,25),wxALIGN_LEFT);
+        m_widget_edits.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(400,100+30*i),wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,30,15));
+    }        
+    for (unsigned int i=0;i<p.cooling_time.size();++i) {
+        if (i>=m_widget_edits.size())
+            break;  // so we don't initialize non-existent widget
+        m_widget_edits[i]->SetValue(p.cooling_time[i]);
+    }
+}
+
+void CoolingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
+    p.cooling_time.clear();
+    for (int i=0;i<4;++i)
+        p.cooling_time.push_back(m_widget_edits[i]->GetValue());
+}
+
+
+
+WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+{
+    const int N = 4; // number of extruders
+    new wxStaticText(this,wxID_ANY,wxString("Volume to wipe when the filament is being"),wxPoint(40,55) ,wxSize(500,25));
+    new wxStaticText(this,wxID_ANY,wxString("unloaded"),wxPoint(110,75) ,wxSize(500,25));
+    new wxStaticText(this,wxID_ANY,wxString("loaded"),wxPoint(195,75) ,wxSize(500,25));        
+    m_widget_button = new wxButton(this,wxID_ANY,"-> Fill in the matrix ->",wxPoint(300,130),wxSize(175,50));
+    for (int i=0;i<N;++i) {
+        new wxStaticText(this,wxID_ANY,wxString("Filament #")<<i+1<<": ",wxPoint(20,105+30*i) ,wxSize(150,25),wxALIGN_LEFT);
+        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,p.filament_wipe_volumes[i].first));
+        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,p.filament_wipe_volumes[i].second));
+    }
+    
+    wxPoint origin(515,55);
+    for (int i=0;i<N;++i) {
+        edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
+        new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(45+60*i,25) ,wxSize(20,25));
+        new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(0,50+30*i) ,wxSize(500,25));
+        for (int j=0;j<N;++j) {
+            edit_boxes.back().push_back(new wxTextCtrl(this,wxID_ANY,wxEmptyString,origin+wxPoint(25+60*i,45+30*j),wxSize(50,25)));
+            if (i==j)
+                edit_boxes[i][j]->Disable();
+            else
+                edit_boxes[i][j]->SetValue(wxString("")<<int(p.wipe_volumes[j][i]));
+        }
+        new wxStaticText(this,wxID_ANY,wxString("Filament changed to"),origin+wxPoint(75,0) ,wxSize(500,25));            
+    }
+    
+    m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){fill_in_matrix();});
+}
+
+void WipingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
+    p.wipe_volumes.clear();
+    p.filament_wipe_volumes.clear();
+    for (int i=0;i<4;++i) {
+        // first go through the full matrix:
+        p.wipe_volumes.push_back(std::vector<float>());
+        for (int j=0;j<4;++j) {
+            double val = 0.;
+            edit_boxes[j][i]->GetValue().ToDouble(&val);
+            p.wipe_volumes[i].push_back((float)val);  
+        }
+        
+        // now the filament volumes:
+        p.filament_wipe_volumes.push_back(std::make_pair(m_old[i]->GetValue(),m_new[i]->GetValue()));
+    }
+}
+
+void WipingPanel::fill_in_matrix() {
+    wxArrayString choices;
+    choices.Add("sum");
+    choices.Add("maximum");
+    wxSingleChoiceDialog dialog(this,"How shall I calculate volume for any given pair?\n\nI can either sum volumes for old and new filament, or just use the higher value.","DEBUGGING",choices);
+    if (dialog.ShowModal() == wxID_CANCEL)
+        return;        
+    for (unsigned i=0;i<4;++i) {
+        for (unsigned j=0;j<4;++j) {
+            if (i==j) continue;
+            if (!dialog.GetSelection()) edit_boxes[j][i]->SetValue(wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue()));
+            else
+              edit_boxes[j][i]->SetValue(wxString("")<< (std::max(m_old[i]->GetValue(), m_new[j]->GetValue())));
+        }
+    }
+}
+
+
+
+GeneralPanel::GeneralPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p) : wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED) {
+    new wxStaticText(this,wxID_ANY,wxString("Maximum bridging over sparse infill (mm):"),wxPoint(100,105) ,wxSize(280,25),wxALIGN_LEFT);
+    m_widget_bridge = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(380,100),wxSize(50,25),wxALIGN_RIGHT|wxSP_ARROW_KEYS,1,50,10);
+    m_widget_adhesion = new wxCheckBox(this,wxID_ANY,"Increased adhesion of first layer",wxPoint(100,150),wxSize(330,25),wxALIGN_RIGHT);
+    m_widget_bridge->SetValue(p.bridging);
+    m_widget_adhesion->SetValue(p.adhesion);
+}
+
+void GeneralPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
+    p.bridging = m_widget_bridge->GetValue();
+    p.adhesion = m_widget_adhesion->GetValue();        
+}
+  
+
+
+
+
+WipeTowerDialog::WipeTowerDialog(wxWindow* parent,const std::string& init_data)
+: wxDialog(parent, -1,  wxT("Wipe tower advanced settings"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+{
+    this->Centre();
+            
+    Slic3r::WipeTowerParameters parameters(init_data);
+    if (!parameters.validate()) {
+        wxMessageDialog(this,"Wipe tower parameters not parsed correctly!\nRestoring default settings.","Error",wxICON_ERROR);
+        parameters.set_defaults();
+    }
+            
+    wxNotebook* notebook = new wxNotebook(this,wxID_ANY,wxPoint(0,0),wxSize(800,450));
+    
+    m_panel_general = new GeneralPanel(notebook,parameters);
+    m_panel_ramming = new RammingPanel(notebook,parameters);
+    m_panel_cooling = new CoolingPanel(notebook,parameters);
+    m_panel_wiping  = new WipingPanel(notebook,parameters);
+    notebook->AddPage(m_panel_general,"General");
+    notebook->AddPage(m_panel_ramming,"Ramming");
+    notebook->AddPage(m_panel_cooling,"Cooling");
+    notebook->AddPage(m_panel_wiping,"Wiping");
+    this->Show();
+
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(notebook, 1, wxEXPAND);
+    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
+    SetSizer(main_sizer);
+    SetMinSize(GetSize());
+    main_sizer->SetSizeHints(this);
+    
+    this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
+    
+    this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
+        m_output_data=read_dialog_values();
+        EndModal(wxID_OK);
+        },wxID_OK);
+}
+
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
new file mode 100644
index 000000000..452886bc3
--- /dev/null
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -0,0 +1,115 @@
+#ifndef _WIPE_TOWER_DIALOG_H_
+#define _WIPE_TOWER_DIALOG_H_
+
+#include <wx/spinctrl.h>
+#include <wx/stattext.h>
+#include <wx/textctrl.h>
+#include <wx/checkbox.h>
+#include <wx/choicdlg.h>
+#include <wx/notebook.h>
+#include <wx/msgdlg.h>
+
+#include "../../libslic3r/GCode/WipeTowerPrusaMM.hpp"
+#include "RammingChart.hpp"
+
+
+// Human-readable output of Parameters structure
+std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par);
+    
+
+class RammingPanel : public wxPanel {
+public:
+    RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+    void fill_parameters(Slic3r::WipeTowerParameters& p);
+
+private:
+    Chart* m_chart = nullptr;
+    wxSpinCtrl* m_widget_volume = nullptr;
+    wxSpinCtrl* m_widget_ramming_line_width_multiplicator = nullptr;
+    wxSpinCtrl* m_widget_ramming_step_multiplicator = nullptr;
+    wxSpinCtrlDouble* m_widget_time = nullptr;
+    wxChoice* m_widget_extruder = nullptr;
+    std::vector<int> m_ramming_step_multiplicators;    
+    std::vector<int> m_ramming_line_width_multiplicators;
+    int m_current_extruder = 0;     // zero-based index
+    
+    void extruder_selection_changed();
+    
+    void line_parameters_changed();
+};
+
+
+
+
+
+
+class CoolingPanel : public wxPanel {
+public:
+    CoolingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+    void fill_parameters(Slic3r::WipeTowerParameters& p);
+    
+private:
+    std::vector<wxSpinCtrl*> m_widget_edits;
+};
+
+
+
+
+
+
+class WipingPanel : public wxPanel {
+public:
+    WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+    void fill_parameters(Slic3r::WipeTowerParameters& p);
+        
+private:
+    void fill_in_matrix();
+        
+    std::vector<wxSpinCtrl*> m_old;
+    std::vector<wxSpinCtrl*> m_new;
+    std::vector<std::vector<wxTextCtrl*>> edit_boxes;
+    wxButton* m_widget_button=nullptr;    
+};
+
+
+
+
+class GeneralPanel : public wxPanel {
+public:
+    GeneralPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+    void fill_parameters(Slic3r::WipeTowerParameters& p);
+    
+private:
+    wxSpinCtrl* m_widget_bridge;
+    wxCheckBox* m_widget_adhesion;
+};
+
+
+
+
+class WipeTowerDialog : public wxDialog {
+public:
+    WipeTowerDialog(wxWindow* parent,const std::string& init_data);
+    
+    std::string GetValue() const { return m_output_data; }
+    
+    
+private:
+    std::string m_file_name="config_wipe_tower";
+    GeneralPanel* m_panel_general = nullptr;
+    RammingPanel* m_panel_ramming = nullptr;
+    CoolingPanel* m_panel_cooling = nullptr;
+    WipingPanel*  m_panel_wiping  = nullptr;
+    std::string m_output_data = "";
+            
+    std::string read_dialog_values() {
+        Slic3r::WipeTowerParameters p;
+        m_panel_general->fill_parameters(p);
+        m_panel_ramming->fill_parameters(p);
+        m_panel_cooling->fill_parameters(p);
+        m_panel_wiping ->fill_parameters(p);
+        return p.to_string();
+    }
+};
+
+#endif  // _WIPE_TOWER_DIALOG_H_
\ No newline at end of file

From 9ea803b000c6de4a4ccfb0d39e88ef69555cb1c1 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 28 Feb 2018 19:53:32 +0100
Subject: [PATCH 017/118] Attempts to fix OSX and Win builds

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 19 ++-----------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 31 +++++++++------------
 xs/src/slic3r/GUI/RammingChart.hpp          |  2 +-
 3 files changed, 17 insertions(+), 35 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index d7492fdbb..004aaf0de 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -31,8 +31,7 @@ TODO LIST
 #define strcasecmp _stricmp
 #endif
 
-constexpr bool  peters_wipe_tower = false;	// sparse wipe tower inspired by Peter's post processor - not finished yet
-constexpr float min_layer_difference = 2*m_perimeter_width;
+const bool  peters_wipe_tower = false;	// sparse wipe tower inspired by Peter's post processor - not finished yet
 
 namespace Slic3r
 {
@@ -468,7 +467,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
     // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
 //	box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
 	//FIXME: set the width properly
-	constexpr float prime_section_width = 60.f;
+	const float prime_section_width = 60.f;
 	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 15.f);
 
 	PrusaMultiMaterial::Writer writer;
@@ -1191,18 +1190,6 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 	depth *= m_extra_spacing;	
 
 	m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth));
-	
-	// Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
-	/*float this_layer_depth = m_plan.back().toolchanges_depth();
-	m_plan.back().depth = this_layer_depth;
-	
-	if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
-		m_wipe_tower_depth = this_layer_depth + m_perimeter_width;
-	for (int i = m_plan.size() - 2; i >= 0 && m_plan[i].depth < this_layer_depth; i-- ) {
-		if ( this_layer_depth - m_plan[i].depth < min_layer_difference && !m_plan[i].tool_changes.empty())
-			m_plan[i].depth += this_layer_depth - m_plan[i].depth;
-		m_plan[i].depth = this_layer_depth;
-	}*/
 }
 
 
@@ -1224,7 +1211,7 @@ void WipeTowerPrusaMM::plan_tower()
 
 		for (int i = layer_index - 1; i >= 0 /*&& m_plan[i].depth < this_layer_depth*/; i--)
 		{
-			if (m_plan[i].depth - this_layer_depth < min_layer_difference )
+			if (m_plan[i].depth - this_layer_depth < 2*m_perimeter_width )
 				m_plan[i].depth = this_layer_depth;
 		}
 	}
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 43163a73a..ef58f6c56 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -10,22 +10,17 @@
 #include "WipeTower.hpp"
 
 // Following is used to calculate extrusion flow - should be taken from config in future
-constexpr float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^3
-constexpr float Nozzle_Diameter = 0.4f;	// nozzle diameter in mm
+const float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^3
+const float Nozzle_Diameter = 0.4f;	// nozzle diameter in mm
 // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
-constexpr float Width_To_Nozzle_Ratio = 1.25f;
+const float Width_To_Nozzle_Ratio = 1.25f;
 
 // m_perimeter_width was hardcoded until now as 0.5 (for 0.4 nozzle and 0.2 layer height)
 // FIXME m_perimeter_width is used in plan_toolchange - take care of proper initialization value when changing to variable
-constexpr float Konst = 1.f;
-constexpr float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Konst;
-
-constexpr float WT_EPSILON = 1e-3f;
-
-
-
-
+const float Konst = 1.f;
+const float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Konst;
 
+const float WT_EPSILON = 1e-3f;
 
 
 namespace Slic3r
@@ -147,14 +142,14 @@ struct WipeTowerParameters {
         ramming_speed.clear();
         ramming_buttons.clear();
         for (unsigned int i=0;i<4;++i) {
-            ramming_speed.push_back(std::vector<float>{7.6, 7.6, 7.6, 7.6, 9.0, 9.0, 9.0, 10.7, 10.7, 10.7});
-            ramming_buttons.push_back(std::vector<std::pair<float,float>>{{0.05, 6.6},{0.45, 6.8},{0.95, 7.8},{1.45, 8.3},{1.95, 9.7},{2.45,10},{2.95, 7.6},{3.45, 7.6},{3.95, 7.6},{4.45, 7.6},{4.95, 7.6}});
+            ramming_speed.push_back(std::vector<float>{7.6f, 7.6f, 7.6f, 7.6f, 9.f, 9.f, 9.f, 10.7f, 10.7f, 10.7f});
+            ramming_buttons.push_back(std::vector<std::pair<float,float>>{{0.05f, 6.6f},{0.45f, 6.8f},{0.95f, 7.8f},{1.45f, 8.3f},{1.95f, 9.7f},{2.45f,10.f},{2.95f, 7.6f},{3.45f, 7.6f},{3.95f, 7.6f},{4.45f, 7.6f},{4.95f, 7.6f}});
         }
-        wipe_volumes = {{  0, 60, 60, 60},
-                        { 60,  0, 60, 60},
-                        { 60, 60,  0, 60},
-                        { 60, 60, 60,  0}};
-        filament_wipe_volumes = {{30,30},{30,30},{30,30},{30,30}};
+        wipe_volumes = {{  0.f, 60.f, 60.f, 60.f},
+                        { 60.f,  0.f, 60.f, 60.f},
+                        { 60.f, 60.f,  0.f, 60.f},
+                        { 60.f, 60.f, 60.f,  0.f}};
+        filament_wipe_volumes = {{30.f,30.f},{30.f,30.f},{30.f,30.f},{30.f,30.f}};
     }
 
     int bridging = 0.f;
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
index 5443e957a..5b7f6daf8 100644
--- a/xs/src/slic3r/GUI/RammingChart.hpp
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -68,7 +68,7 @@ private:
 
     class ButtonToDrag {
     public:
-        bool operator<(ButtonToDrag& a) { return m_pos.m_x < a.m_pos.m_x; }
+        bool operator<(const ButtonToDrag& a) const { return m_pos.m_x < a.m_pos.m_x; }
         ButtonToDrag(wxPoint2DDouble pos) : m_pos{pos} {};
         wxPoint2DDouble get_pos() const { return m_pos; }            
         void move(double x,double y) { m_pos.m_x+=x; m_pos.m_y+=y; }

From b1b16359d9b54d225742233ebb45d67bb6243094 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 1 Mar 2018 08:43:43 +0100
Subject: [PATCH 018/118] Changes in includes in order to fix Win builds

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 004aaf0de..fdfd8752f 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -20,6 +20,8 @@ TODO LIST
 #include <fstream>
 #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 ef58f6c56..58e5c0ee6 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -1,7 +1,6 @@
 #ifndef WipeTowerPrusaMM_hpp_
 #define WipeTowerPrusaMM_hpp_
 
-#include <algorithm>
 #include <cmath>
 #include <string>
 #include <sstream>

From c76c075569effe7b20f28c3217f8fd5c568b890d Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 1 Mar 2018 09:19:34 +0100
Subject: [PATCH 019/118] Yet another attempt to fix Win builds

---
 xs/src/slic3r/GUI/RammingChart.hpp    | 2 +-
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
index 5b7f6daf8..e04f4b52e 100644
--- a/xs/src/slic3r/GUI/RammingChart.hpp
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -98,7 +98,7 @@ private:
     int which_button_is_clicked(const wxPoint& point) const {
         if (!m_rect.Contains(point))
             return -1;
-        for (uint i=0;i<m_buttons[m_current_extruder].size();++i) {
+        for (unsigned int i=0;i<m_buttons[m_current_extruder].size();++i) {
             wxRect rect(math_to_screen(m_buttons[m_current_extruder][i].get_pos())-wxPoint(side/2.,side/2.),wxSize(side,side)); // bounding rectangle of this button
             if ( rect.Contains(point) )
                 return i;
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 5b188e6d1..323749b0a 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -1,3 +1,4 @@
+#include <algorithm>
 #include "WipeTowerDialog.hpp"
 
 // Human-readable output of Parameters structure

From 1d787a15a0cc3ef798bf993b8c1202d3c747bdd1 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 1 Mar 2018 09:57:51 +0100
Subject: [PATCH 020/118] Fix for win builds

---
 xs/src/slic3r/GUI/RammingChart.cpp | 4 ++--
 xs/src/slic3r/GUI/RammingChart.hpp | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
index 3b5f4e41e..3b535aacf 100644
--- a/xs/src/slic3r/GUI/RammingChart.cpp
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -17,13 +17,13 @@ void Chart::draw(wxDC& dc) {
     
     
     if (!m_line_to_draw->empty()) {
-        for (uint i=0;i<m_line_to_draw->size()-2;++i) {
+        for (unsigned int i=0;i<m_line_to_draw->size()-2;++i) {
             int color = 510*((m_rect.GetBottom()-(*m_line_to_draw)[i])/double(m_rect.GetHeight()));
             dc.SetPen( wxPen( wxColor(std::min(255,color),255-std::max(color-255,0),0), 1 ) );
             dc.DrawLine(m_rect.GetLeft()+1+i,(*m_line_to_draw)[i],m_rect.GetLeft()+1+i,m_rect.GetBottom());        
         }
         dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
-        for (uint i=0;i<m_line_to_draw->size()-2;++i) {
+        for (unsigned int i=0;i<m_line_to_draw->size()-2;++i) {
             if (splines)
                 dc.DrawLine(m_rect.GetLeft()+i,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i+1]);        
             else {
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
index e04f4b52e..8274b96e6 100644
--- a/xs/src/slic3r/GUI/RammingChart.hpp
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -111,7 +111,7 @@ private:
     void recalculate_volume();
      
     
-    unsigned m_current_extruder = 0;
+    unsigned int m_current_extruder = 0;
     wxRect m_rect;                  // rectangle on screen the chart is mapped into (screen coordinates)
     wxPoint m_previous_mouse;        
     std::vector< std::vector<ButtonToDrag> > m_buttons;

From d2006c8d8ec32735fb8518855bc7df18cf502047 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 1 Mar 2018 16:15:00 +0100
Subject: [PATCH 021/118] Cooling tubes parameters added into GUI and
 configuration layer (not yet into wipe tower generator)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  8 ++++++-
 xs/src/libslic3r/Print.cpp                  |  8 +++++--
 xs/src/libslic3r/PrintConfig.cpp            | 25 +++++++++++++++++++++
 xs/src/libslic3r/PrintConfig.hpp            |  6 +++++
 xs/src/slic3r/GUI/Preset.cpp                |  2 +-
 xs/src/slic3r/GUI/Tab.cpp                   |  3 +++
 6 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 58e5c0ee6..c05f2aebe 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -188,7 +188,9 @@ public:
 	// y			-- y coordinates of wipe tower in mm ( left bottom corner )
 	// width		-- width of wipe tower in mm ( default 60 mm - leave as it is )
 	// wipe_area	-- space available for one toolchange in mm
-	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, unsigned int initial_tool,std::string& parameters) :
+	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, float cooling_tube_retraction,
+                     float cooling_tube_length, float parking_pos_retraction, std::string& parameters,
+                     unsigned int initial_tool) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
 		m_wipe_tower_rotation_angle(rotation_angle),
@@ -199,6 +201,7 @@ public:
 		m_current_tool(initial_tool),
         m_par(parameters)
  	{
+        printf("Jsem konstruktor WT a vidim cisla %f,%f,%f\n",cooling_tube_retraction,cooling_tube_length,parking_pos_retraction);
 		for (size_t i = 0; i < 4; ++ i) {
 			// Extruder specific parameters.
 			m_material[i] = PLA;
@@ -325,6 +328,9 @@ private:
 	// G-code generator parameters.
 	float  			m_zhop 			 = 0.5f;
 	float  			m_retract		 = 4.f;
+    float           m_cooling_tube_retraction   = 0.f;
+    float           m_cooling_tube_length       = 0.f;
+    float           m_parking_pos_retraction    = 0.f;
 	
 	float m_line_width = Nozzle_Diameter * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
 	float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index fe98612cf..020af7610 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -90,6 +90,8 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
         "bridge_acceleration",
         "bridge_fan_speed",
         "cooling",
+        "cooling_tube_retraction",
+        "cooling_tube_length",
         "default_acceleration",
         "deretract_speed",
         "disable_fan_first_layers",
@@ -128,6 +130,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
         "notes",
         "only_retract_when_crossing_perimeters",
         "output_filename_format",
+        "parking_pos_retraction",
         "perimeter_acceleration",
         "post_process",
         "printer_notes",
@@ -1022,8 +1025,9 @@ void Print::_make_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_per_color_wipe.value),
-        float(this->config.wipe_tower_rotation_angle.value), m_tool_ordering.first_extruder(),
-        this->config.wipe_tower_advanced.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),
+        this->config.wipe_tower_advanced.value,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 7a48b3a32..a5fbb844b 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -171,6 +171,22 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "cooling!";
     def->default_value = new ConfigOptionBools { true };
 
+    def = this->add("cooling_tube_retraction", coFloat);
+    def->label = _L("Cooling tube position");
+    def->tooltip = _L("Distance of the center-point of the cooling tube from the extruder tip ");
+    def->sidetext = _L("mm");
+    def->cli = "cooling_tube_retraction=f";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(0);
+
+    def = this->add("cooling_tube_length", coFloat);
+    def->label = _L("Cooling tube length");
+    def->tooltip = _L("Length of the cooling tube to limit space for cooling moves inside it ");
+    def->sidetext = _L("mm");
+    def->cli = "cooling_tube_length=f";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(0);
+
     def = this->add("default_acceleration", coFloat);
     def->label = _L("Default");
     def->tooltip = _L("This is the acceleration your printer will be reset to after "
@@ -947,6 +963,15 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "overhangs!";
     def->default_value = new ConfigOptionBool(true);
 
+    def = this->add("parking_pos_retraction", coFloat);
+    def->label = _L("Filament parking position");
+    def->tooltip = _L("Distance of the extruder tip from the position where the filament is parked"
+                      "when unloaded. This should match the value in printer firmware. ");
+    def->sidetext = _L("mm");
+    def->cli = "parking_pos_retraction=f";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(0);
+
     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 d55aafed6..c556a2d69 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -491,6 +491,9 @@ public:
     ConfigOptionBool                use_relative_e_distances;
     ConfigOptionBool                use_volumetric_e;
     ConfigOptionBool                variable_layer_height;
+    ConfigOptionFloat               cooling_tube_retraction;
+    ConfigOptionFloat               cooling_tube_length;
+    ConfigOptionFloat               parking_pos_retraction;
     
     std::string get_extrusion_axis() const
     {
@@ -540,6 +543,9 @@ protected:
         OPT_PTR(use_relative_e_distances);
         OPT_PTR(use_volumetric_e);
         OPT_PTR(variable_layer_height);
+        OPT_PTR(cooling_tube_retraction);
+        OPT_PTR(cooling_tube_length);
+        OPT_PTR(parking_pos_retraction);
     }
 };
 
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index be31c1bd1..c363647cb 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -227,7 +227,7 @@ const std::vector<std::string>& Preset::printer_options()
             "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", 
             "octoprint_host", "octoprint_apikey", "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_notes"
+            "between_objects_gcode", "printer_notes", "cooling_tube_retraction", "cooling_tube_length", "parking_pos_retraction"
         };
         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 7e1ad5dc5..790f5dc4b 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -1139,6 +1139,9 @@ void TabPrinter::build()
 		optgroup->append_single_option_line("use_firmware_retraction");
 		optgroup->append_single_option_line("use_volumetric_e");
 		optgroup->append_single_option_line("variable_layer_height");
+        optgroup->append_single_option_line("cooling_tube_retraction");
+		optgroup->append_single_option_line("cooling_tube_length");
+		optgroup->append_single_option_line("parking_pos_retraction");
 
 	page = add_options_page(_L("Custom G-code"), "cog.png");
 		optgroup = page->new_optgroup(_L("Start G-code"), 0);

From e1421da5e82936bf66cdee7802c1e78481b9983e Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 2 Mar 2018 13:26:16 +0100
Subject: [PATCH 022/118] Integrating cooling tube parameters into wipe tower
 generator

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 25 +++++++++++++--------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  7 +++++-
 xs/src/libslic3r/PrintConfig.cpp            |  6 ++---
 3 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index fdfd8752f..2e3e7c0a7 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -841,14 +841,17 @@ void WipeTowerPrusaMM::toolchange_Unload(
 			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, e0, 1.74f * e, 5200);
 	}*/
 }
-	// Pull the filament end into a cooling tube.
-	writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
+	// Pull the filament end into a cooling tube (Alex)
+	//writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
+
+    // Pull the filament end to the BEGINNING of the cooling tube
+    writer.retract(15, 5000).retract(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42, 5400).retract(15, 3000).retract(12, 2000);
+
 
 	if (new_temperature != 0) 	// Set the extruder temperature, but don't wait.
 		writer.set_extruder_temp(new_temperature, false);
 
 // cooling:
-	writer.retract(2, 2000);
 	writer.suppress_preview();
 	writer.travel(writer.x(), writer.y() + y_step);
 	const float start_x = writer.x();
@@ -860,9 +863,10 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	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,10.f); // distance to travel
+		const float e_dist = std::min(speed * time,2*m_cooling_tube_length); // distance to travel
 		
-		if (speed * time < 10.f) { 	// this move is the last one at this speed
+		// 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_par.cooling_time[m_current_tool] / N;
 		}
@@ -874,7 +878,9 @@ void WipeTowerPrusaMM::toolchange_Unload(
 		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.retract(-2, 2000);
+    // 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);
+
 	writer.travel(writer.x(), writer.y() - y_step,2400);
 
 	// Alex's old cooling:
@@ -944,8 +950,8 @@ void WipeTowerPrusaMM::toolchange_Load(
 {	
 	float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f;
 	float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f;
-	float oldx=writer.x();	// the nozzle is in place to do the first wiping moves, we will remember the position
-	float oldy=writer.y();
+	float oldx = writer.x();	// the nozzle is in place to do the first wiping moves, we will remember the position
+	float oldy = writer.y();
 
 	writer.append("; CP TOOLCHANGE LOAD\n")
 	// Load the filament while moving left / right,
@@ -954,7 +960,8 @@ void WipeTowerPrusaMM::toolchange_Load(
 		  // Accelerate the filament loading
 		  .load_move_x(xr, 20, 1400)
 		  // Fast loading phase
-		  .load_move_x(xl, 40, 3000)
+          //.load_move_x(xl, 40, 3000) - Alex
+		  .load_move_x(xl,m_parking_pos_retraction-50-2,3000) // loading is 2mm shorter that previous retraction
 		  // Slowing down
 		  .load_move_x(xr, 20, 1600)
 		  .load_move_x(xl, 10, 1000)
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index c05f2aebe..7f4cc8a30 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -102,6 +102,9 @@ struct WipeTowerParameters {
         for (unsigned int i=0;i<vect.size();++i)
             if (i%2==1)
                 filament_wipe_volumes.push_back(std::make_pair(vect[i-1],vect[i]));
+
+        if (!validate())    // in case we did not parse the input right
+            set_defaults();
     }
 
     std::string to_string() {
@@ -198,10 +201,12 @@ public:
 		m_z_pos(0.f),
 		m_is_first_layer(false),
 		m_is_last_layer(false),
+        m_cooling_tube_retraction(cooling_tube_retraction),
+        m_cooling_tube_length(cooling_tube_length),
+        m_parking_pos_retraction(parking_pos_retraction),
 		m_current_tool(initial_tool),
         m_par(parameters)
  	{
-        printf("Jsem konstruktor WT a vidim cisla %f,%f,%f\n",cooling_tube_retraction,cooling_tube_length,parking_pos_retraction);
 		for (size_t i = 0; i < 4; ++ i) {
 			// Extruder specific parameters.
 			m_material[i] = PLA;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index a5fbb844b..1886d52b8 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -177,7 +177,7 @@ PrintConfigDef::PrintConfigDef()
     def->sidetext = _L("mm");
     def->cli = "cooling_tube_retraction=f";
     def->min = 0;
-    def->default_value = new ConfigOptionFloat(0);
+    def->default_value = new ConfigOptionFloat(91.5f);
 
     def = this->add("cooling_tube_length", coFloat);
     def->label = _L("Cooling tube length");
@@ -185,7 +185,7 @@ PrintConfigDef::PrintConfigDef()
     def->sidetext = _L("mm");
     def->cli = "cooling_tube_length=f";
     def->min = 0;
-    def->default_value = new ConfigOptionFloat(0);
+    def->default_value = new ConfigOptionFloat(5.f);
 
     def = this->add("default_acceleration", coFloat);
     def->label = _L("Default");
@@ -970,7 +970,7 @@ PrintConfigDef::PrintConfigDef()
     def->sidetext = _L("mm");
     def->cli = "parking_pos_retraction=f";
     def->min = 0;
-    def->default_value = new ConfigOptionFloat(0);
+    def->default_value = new ConfigOptionFloat(92.f);
 
     def = this->add("perimeter_acceleration", coFloat);
     def->label = _L("Perimeters");

From b3e9b82280557c9803c270854edb4935287a840e Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 2 Mar 2018 15:52:16 +0100
Subject: [PATCH 023/118] (Un)loading speed and time delay parameters
 introduced into GUI and conf. layer (not yet into wipe tower generator)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 21 ++++++++--------
 xs/src/libslic3r/PrintConfig.cpp            | 27 +++++++++++++++++++++
 xs/src/libslic3r/PrintConfig.hpp            |  6 +++++
 xs/src/slic3r/GUI/Preset.cpp                |  9 +++----
 xs/src/slic3r/GUI/Tab.cpp                   | 12 +++++----
 5 files changed, 55 insertions(+), 20 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 2e3e7c0a7..c146f6030 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -845,7 +845,11 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	//writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
 
     // Pull the filament end to the BEGINNING of the cooling tube
-    writer.retract(15, 5000).retract(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42, 5400).retract(15, 3000).retract(12, 2000);
+    float unloading_feedrate = 5400.f; // Alex's original feedrate was 5400
+    writer.retract(15, 5000)    // just after ramming - fixed speed
+          .retract(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42, unloading_feedrate)
+          .retract(15, unloading_feedrate*0.55f)
+          .retract(12, unloading_feedrate*0.35f);
 
 
 	if (new_temperature != 0) 	// Set the extruder temperature, but don't wait.
@@ -953,17 +957,14 @@ void WipeTowerPrusaMM::toolchange_Load(
 	float oldx = writer.x();	// the nozzle is in place to do the first wiping moves, we will remember the position
 	float oldy = writer.y();
 
-	writer.append("; CP TOOLCHANGE LOAD\n")
-	// Load the filament while moving left / right,
+    // Load the filament while moving left / right,
 	// so the excess material will not create a blob at a single position.
+    float loading_feedrate = 3000.f;
+	writer.append("; CP TOOLCHANGE LOAD\n")
 		  .suppress_preview()
-		  // Accelerate the filament loading
-		  .load_move_x(xr, 20, 1400)
-		  // Fast loading phase
-          //.load_move_x(xl, 40, 3000) - Alex
-		  .load_move_x(xl,m_parking_pos_retraction-50-2,3000) // loading is 2mm shorter that previous retraction
-		  // Slowing down
-		  .load_move_x(xr, 20, 1600)
+		  .load_move_x(xr, 20, 1400)  // Accelerate the filament loading
+		  .load_move_x(xl,m_parking_pos_retraction-50-2,3000) // Fast phase - loading is 2mm shorter that previous retraction
+		  .load_move_x(xr, 20, 1600)    // Slowing down
 		  .load_move_x(xl, 10, 1000)
 		  .travel(oldx,oldy)
 		  .resume_preview();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 1886d52b8..cebb0832b 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -448,6 +448,33 @@ PrintConfigDef::PrintConfigDef()
     def->min = 0;
     def->default_value = new ConfigOptionFloats { 0. };
 
+    def = this->add("filament_loading_speed", coFloats);
+    def->label = _L("Loading speed");
+    def->tooltip = _L("Speed used for loading the filament on the wipe tower. ");
+    def->sidetext = _L("mm\u00B3/s");
+    def->cli = "filament-loading-speed=f@";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloats { 0. };
+
+    def = this->add("filament_unloading_speed", coFloats);
+    def->label = _L("Unloading speed");
+    def->tooltip = _L("Speed used for unloading the filament on the wipe tower (does not affect "
+                      " initial part of unloading just after ramming). ");
+    def->sidetext = _L("mm\u00B3/s");
+    def->cli = "filament-unloading-speed=f@";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloats { 0. };
+
+    def = this->add("filament_toolchange_delay", coFloats);
+    def->label = _L("Delay after unloading");
+    def->tooltip = _L("Time to wait after the filament is unloaded. "
+                   "May help to get reliable toolchanges with flexible materials "
+                   "that may need more time to shrink to original dimensions. ");
+    def->sidetext = _L("s");
+    def->cli = "filament-toolchange-delay=f@";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloats { 0. };
+
     def = this->add("filament_diameter", coFloats);
     def->label = _L("Diameter");
     def->tooltip = _L("Enter your filament diameter here. Good precision is required, so use a caliper "
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index c556a2d69..407832419 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -466,6 +466,9 @@ public:
     ConfigOptionBools               filament_soluble;
     ConfigOptionFloats              filament_cost;
     ConfigOptionFloats              filament_max_volumetric_speed;
+    ConfigOptionFloats              filament_loading_speed;
+    ConfigOptionFloats              filament_unloading_speed;
+    ConfigOptionFloats              filament_toolchange_delay;
     ConfigOptionBool                gcode_comments;
     ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
     ConfigOptionString              layer_gcode;
@@ -518,6 +521,9 @@ protected:
         OPT_PTR(filament_soluble);
         OPT_PTR(filament_cost);
         OPT_PTR(filament_max_volumetric_speed);
+        OPT_PTR(filament_loading_speed);
+        OPT_PTR(filament_unloading_speed);
+        OPT_PTR(filament_toolchange_delay);
         OPT_PTR(gcode_comments);
         OPT_PTR(gcode_flavor);
         OPT_PTR(layer_gcode);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index c363647cb..1cfd73bab 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -210,11 +210,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", "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"
+        "extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_unloading_speed", "filament_toolchange_delay",
+        "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"
     };
     return s_opts;
 }
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 790f5dc4b..92504f714 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -441,7 +441,6 @@ void TabPrint::build()
 		optgroup->append_single_option_line("ooze_prevention");
 		optgroup->append_single_option_line("standby_temperature_delta");
 
-    if (true) {
 		optgroup = page->new_optgroup(_L("Wipe tower"));
 		optgroup->append_single_option_line("wipe_tower");
 		optgroup->append_single_option_line("wipe_tower_x");
@@ -449,8 +448,8 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_width");
 		optgroup->append_single_option_line("wipe_tower_per_color_wipe");
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
-        Line line{ _L("Advanced"), "" };
-		line.widget = [this](wxWindow* parent){
+        line = { _L("Advanced"), "" };
+        line.widget = [this](wxWindow* parent){
 			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _L("Advanced settings")+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
 			auto sizer = new wxBoxSizer(wxHORIZONTAL);
 			sizer->Add(m_wipe_tower_btn);
@@ -468,8 +467,6 @@ void TabPrint::build()
 			return sizer;
 		};
 		optgroup->append_line(line);
-    }
-
 
 		optgroup = page->new_optgroup(_L("Advanced"));
 		optgroup->append_single_option_line("interface_shells");
@@ -867,6 +864,11 @@ void TabFilament::build()
 		};
 		optgroup->append_line(line);
 
+        optgroup = page->new_optgroup(_L("Toolchange behaviour"));
+		optgroup->append_single_option_line("filament_loading_speed");
+        optgroup->append_single_option_line("filament_unloading_speed");
+        optgroup->append_single_option_line("filament_toolchange_delay");
+
 	page = add_options_page(_L("Custom G-code"), "cog.png");
 		optgroup = page->new_optgroup(_L("Start G-code"), 0);
 		Option option = optgroup->get_option("start_filament_gcode");

From 5aca3045b9619903ade59fa0da22435ee21ea4e6 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 5 Mar 2018 10:45:35 +0100
Subject: [PATCH 024/118] (Un)loading speed and delay introduced into wipe
 tower generator

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 50 ++++++++++++++-------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 35 ++++++++++-----
 xs/src/libslic3r/Print.cpp                  |  8 +++-
 xs/src/libslic3r/PrintConfig.cpp            |  8 ++--
 4 files changed, 68 insertions(+), 33 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index c146f6030..bc93a938e 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -243,6 +243,17 @@ public:
 		return *this;
 	};
 
+    // Wait for a period of time (seconds).
+	Writer& wait(float time)
+	{
+        if (time==0)
+            return *this;
+		char buf[128];
+		sprintf(buf, "G4 S%.3f\n", time);
+		m_gcode += buf;
+		return *this;
+	};
+
 	// Set speed factor override percentage.
 	Writer& speed_override(int speed) 
 	{
@@ -495,7 +506,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 		for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
 			unsigned int tool = tools[idx_tool];
 			m_left_to_right = true;
-			toolchange_Change(writer, tool, m_material[tool]); // Select the tool, set a speed override for soluble and flex materials.
+			toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials.
 			toolchange_Load(writer, cleaning_box); // Prime the tool.
 			if (idx_tool + 1 == tools.size()) {
 				// Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
@@ -506,7 +517,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 				toolchange_Wipe(writer, cleaning_box , 5.f);
 				box_coordinates box = cleaning_box;
 				box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width);
-				toolchange_Unload(writer, box , m_material[m_current_tool], m_first_layer_temperature[tools[idx_tool + 1]]);
+				toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
 				cleaning_box.translate(prime_section_width, 0.f);
 				writer.travel(cleaning_box.ld, 7200);
 			}
@@ -614,7 +625,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.append(";--------------------\n"
 				"; CP TOOLCHANGE START\n")
 		.comment_with_value(" toolchange #", m_num_tool_changes)
-		.comment_material(m_material[m_current_tool])
+		.comment_material(m_filpar[m_current_tool].material)
 		.append(";--------------------\n")
 		.speed_override(100);
 
@@ -622,7 +633,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 
 	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
 		// Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower.
-		float initial_retract = ((m_material[m_current_tool] == SCAFF) ? 1.f : 0.5f) * m_retract;
+		float initial_retract = ((m_filpar[m_current_tool].material == SCAFF) ? 1.f : 0.5f) * m_retract;
 		writer 	// Lift for a Z hop.
 		  	  	.z_hop(m_zhop, 7200)
 		  		// Additional retract on move to tower.
@@ -650,9 +661,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		
 		// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
 		if (tool != (unsigned int)-1){ 			// This is not the last change.
-			toolchange_Unload(writer, cleaning_box, m_material[m_current_tool],
-							  m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);			
-			toolchange_Change(writer, tool, m_material[tool]); // Change the tool, set a speed override for soluble and flex materials.
+			toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
+							  m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
+			toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
 			toolchange_Load(writer, cleaning_box);			
 			toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
 
@@ -661,7 +672,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 			writer.travel(box.ru, 7200)
 					.travel(box.lu);*/
 		} else
-			toolchange_Unload(writer, cleaning_box, m_material[m_current_tool], m_temperature[m_current_tool]);
+			toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
 
 		if (last_change_in_layer) // draw perimeter line
 			writer.travel(m_wipe_tower_pos, 7000)
@@ -845,8 +856,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	//writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
 
     // Pull the filament end to the BEGINNING of the cooling tube
-    float unloading_feedrate = 5400.f; // Alex's original feedrate was 5400
-    writer.retract(15, 5000)    // just after ramming - fixed speed
+    float unloading_feedrate = 60.f * m_filpar[m_current_tool].unloading_speed;
+    writer.retract(15, 5000)                              // just after ramming - always the same speed
           .retract(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42, unloading_feedrate)
           .retract(15, unloading_feedrate*0.55f)
           .retract(12, unloading_feedrate*0.35f);
@@ -882,6 +893,9 @@ void WipeTowerPrusaMM::toolchange_Unload(
 		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);
 	}
+
+    // 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);
 
@@ -957,15 +971,17 @@ void WipeTowerPrusaMM::toolchange_Load(
 	float oldx = writer.x();	// the nozzle is in place to do the first wiping moves, we will remember the position
 	float oldy = writer.y();
 
-    // Load the filament while moving left / right,
-	// so the excess material will not create a blob at a single position.
-    float loading_feedrate = 3000.f;
+    // 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
 	writer.append("; CP TOOLCHANGE LOAD\n")
 		  .suppress_preview()
-		  .load_move_x(xr, 20, 1400)  // Accelerate the filament loading
-		  .load_move_x(xl,m_parking_pos_retraction-50-2,3000) // Fast phase - loading is 2mm shorter that previous retraction
-		  .load_move_x(xr, 20, 1600)    // Slowing down
-		  .load_move_x(xl, 10, 1000)
+		  .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(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
 		  .travel(oldx,oldy)
 		  .resume_preview();
 
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 7f4cc8a30..893e4a125 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -209,9 +209,9 @@ public:
  	{
 		for (size_t i = 0; i < 4; ++ i) {
 			// Extruder specific parameters.
-			m_material[i] = PLA;
-			m_temperature[i] = 0;
-			m_first_layer_temperature[i] = 0;
+			m_filpar[i].material = PLA;
+			m_filpar[i].temperature = 0;
+			m_filpar[i].first_layer_temperature = 0;
 		}
 	}
 
@@ -225,11 +225,15 @@ public:
 
 
 	// Set the extruder properties.
-	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp)
+	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
+                      float unloading_speed, float delay)
 	{
-		m_material[idx] = material;
-		m_temperature[idx] = temp;
-		m_first_layer_temperature[idx] = first_layer_temp;
+        m_filpar[idx].material = material;
+        m_filpar[idx].temperature = temp;
+        m_filpar[idx].first_layer_temperature = first_layer_temp;
+        m_filpar[idx].loading_speed = loading_speed;
+        m_filpar[idx].unloading_speed = unloading_speed;
+        m_filpar[idx].delay = delay;
 	}
 
 
@@ -336,14 +340,23 @@ private:
     float           m_cooling_tube_retraction   = 0.f;
     float           m_cooling_tube_length       = 0.f;
     float           m_parking_pos_retraction    = 0.f;
-	
+
 	float m_line_width = Nozzle_Diameter * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
 	float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
 
+
+    struct FilamentParameters {
+        material_type 	material;
+        int  			temperature;
+        int  			first_layer_temperature;
+        float           loading_speed;
+        float           unloading_speed;
+        float           delay;
+    };
+
 	// Extruder specific parameters.
-	material_type 	m_material[4];
-	int  			m_temperature[4];
-	int  			m_first_layer_temperature[4];
+    FilamentParameters m_filpar[4];
+
 
 	// State of the wiper tower generator.
 	unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 020af7610..447aec750 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -185,6 +185,9 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "filament_type"
             || opt_key == "filament_soluble"
             || opt_key == "first_layer_temperature"
+            || opt_key == "filament_loading_speed"
+            || opt_key == "filament_unloading_speed"
+            || opt_key == "filament_toolchange_delay"
             || opt_key == "gcode_flavor"
             || opt_key == "single_extruder_multi_material"
             || opt_key == "spiral_vase"
@@ -1038,7 +1041,10 @@ void Print::_make_wipe_tower()
             i, 
             WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
             this->config.temperature.get_at(i),
-            this->config.first_layer_temperature.get_at(i));
+            this->config.first_layer_temperature.get_at(i),
+            this->config.filament_loading_speed.get_at(i),
+            this->config.filament_unloading_speed.get_at(i),
+            this->config.filament_toolchange_delay.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.
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index cebb0832b..40b348fb8 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -451,19 +451,19 @@ PrintConfigDef::PrintConfigDef()
     def = this->add("filament_loading_speed", coFloats);
     def->label = _L("Loading speed");
     def->tooltip = _L("Speed used for loading the filament on the wipe tower. ");
-    def->sidetext = _L("mm\u00B3/s");
+    def->sidetext = _L("mm/s");
     def->cli = "filament-loading-speed=f@";
     def->min = 0;
-    def->default_value = new ConfigOptionFloats { 0. };
+    def->default_value = new ConfigOptionFloats { 28. };
 
     def = this->add("filament_unloading_speed", coFloats);
     def->label = _L("Unloading speed");
     def->tooltip = _L("Speed used for unloading the filament on the wipe tower (does not affect "
                       " initial part of unloading just after ramming). ");
-    def->sidetext = _L("mm\u00B3/s");
+    def->sidetext = _L("mm/s");
     def->cli = "filament-unloading-speed=f@";
     def->min = 0;
-    def->default_value = new ConfigOptionFloats { 0. };
+    def->default_value = new ConfigOptionFloats { 90. };
 
     def = this->add("filament_toolchange_delay", coFloats);
     def->label = _L("Delay after unloading");

From 4058f002751477b677d4f547eed1af459d7ea7ee Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 5 Mar 2018 13:53:49 +0100
Subject: [PATCH 025/118] Added print head moves after ramming and removed one
 unnecessary diagonal move

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 25 ++++++++++++++-------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index bc93a938e..f87db98a7 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -675,11 +675,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 			toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
 
 		if (last_change_in_layer) // draw perimeter line
-			writer.travel(m_wipe_tower_pos, 7000)
-				.extrude(m_wipe_tower_pos + xy(0, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width), 3200)
-				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width))
-				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, 0))
-				.extrude(m_wipe_tower_pos);
+			writer.travel(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width), 7000)
+				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, 0), 3200)
+				.extrude(m_wipe_tower_pos)
+				.extrude(m_wipe_tower_pos + xy(0, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width))
+				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width));
 
 		// Reset the extruder current to a normal value.
 		writer.set_extruder_trimpot(550)
@@ -856,12 +856,21 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	//writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
 
     // Pull the filament end to the BEGINNING of the cooling tube
-    float unloading_feedrate = 60.f * m_filpar[m_current_tool].unloading_speed;
+    /*float unloading_feedrate = 60.f * m_filpar[m_current_tool].unloading_speed;
     writer.retract(15, 5000)                              // just after ramming - always the same speed
           .retract(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42, unloading_feedrate)
           .retract(15, unloading_feedrate*0.55f)
-          .retract(12, unloading_feedrate*0.35f);
+          .retract(12, unloading_feedrate*0.35f);*/
 
+    // 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);
+    writer.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)/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(oldx         ,-12    , 60.f * std::hypot(xdist,12)/12       * m_filpar[m_current_tool].unloading_speed*0.35f );
 
 	if (new_temperature != 0) 	// Set the extruder temperature, but don't wait.
 		writer.set_extruder_temp(new_temperature, false);
@@ -870,7 +879,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	writer.suppress_preview();
 	writer.travel(writer.x(), writer.y() + y_step);
 	const float start_x = writer.x();
-	const float turning_point = ( xr-start_x > start_x-xl ? xr : xl );
+	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,(m_par.cooling_time[m_current_tool]-14)/3);
 	float time = m_par.cooling_time[m_current_tool] / N;

From 6c223c2f8464f92462bdaadc70dbee822b69560f Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 5 Mar 2018 16:51:31 +0100
Subject: [PATCH 026/118] Fixed cooling time calculation, removed unnecessary
 diagonal moves, fixed 'stringing' on start and end of narrower wipe tower
 layers

---
 xs/src/libslic3r/GCode/WipeTower.hpp        |  2 +-
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 20 ++++++++++----------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index 289e8e122..e193fd972 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -17,7 +17,7 @@ public:
 	struct xy
 	{
 		xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
-		xy(xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
+		xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
 		xy  operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
 		xy  operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
 		xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index f87db98a7..8e548a4a4 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -93,8 +93,8 @@ public:
 	float                x()     const { return m_current_pos.x; }
 	float                y()     const { return m_current_pos.y; }
 	const WipeTower::xy& pos()   const { return m_current_pos; }
-	const WipeTower::xy	 start_pos_rotated() const { return m_start_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
-	const WipeTower::xy  pos_rotated() const { return m_current_pos.rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
+	const WipeTower::xy	 start_pos_rotated() const { return WipeTower::xy(m_start_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
+	const WipeTower::xy  pos_rotated() const { return WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
 
 	// Extrude with an explicitely provided amount of extrusion.
@@ -882,7 +882,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	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,(m_par.cooling_time[m_current_tool]-14)/3);
-	float time = m_par.cooling_time[m_current_tool] / N;
+	float time = m_par.cooling_time[m_current_tool] / float(N);
 
 	i = 0;
 	while (i<N) {
@@ -891,8 +891,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
 		
 		// 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_par.cooling_time[m_current_tool] / N;
+            ++i;
+			time = m_par.cooling_time[m_current_tool] / float(N);
 		}
 		else
 			time -= e_dist / speed; // subtract time this part will really take
@@ -1112,7 +1112,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 		}
 	} else {
 		// The print head is inside the wipe tower. Rather move to the start of the following extrusion.
-		writer.set_initial_position(fill_box.ld);
+		writer.set_initial_position(fill_box.lu);
 	}
 
 	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
@@ -1124,11 +1124,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 			}
 			else i=2;	// only draw the inner perimeter
 
-			writer.travel(box.ld,7200)
-			    .extrude(box.lu, 2400 * speed_factor)
-			    .extrude(box.ru)
+			writer.travel(box.lu,7200)
+			    .extrude(box.ru, 2400 * speed_factor)
 			    .extrude(box.rd)
-			    .extrude(box.ld);
+			    .extrude(box.ld)
+			    .extrude(box.lu);
 		}
 
 		if (m_is_first_layer && m_par.adhesion) {

From af281e13dbf6a1a295246adae5aed633097a69db Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 6 Mar 2018 19:14:12 +0100
Subject: [PATCH 027/118] Last wipe on layer accounts of border and sparse
 infill (ugly, yet working)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 105 +++++++++++++-------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |   8 +-
 2 files changed, 76 insertions(+), 37 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 8e548a4a4..d6e3013e2 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -613,7 +613,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	box_coordinates cleaning_box(
 		m_wipe_tower_pos + xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
 		m_wipe_tower_width - m_perimeter_width,
-		(tool != (unsigned int)(-1) ? m_layer_info->depth : m_wipe_tower_depth-m_perimeter_width));
+		(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;
 	writer.set_extrusion_flow(m_extrusion_flow)
@@ -1049,11 +1050,10 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 			writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
 		else
 			writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
-		
-		/*if ((m_current_shape == SHAPE_NORMAL) ?		// in case next line would not fit
-			(writer.y() > cleaning_box.lu.y - m_perimeter_width * 1.5f) :
-			(writer.y() < cleaning_box.ld.y + m_perimeter_width * 1.5f))
-			break;*/
+
+        if (writer.y()+EPSILON > cleaning_box.lu.y-0.5f*m_perimeter_width)
+            break;		// in case next line would not fit
+
 		traversed_x -= writer.x();
 		x_to_wipe -= fabs(traversed_x);
 		if (x_to_wipe < WT_EPSILON) {
@@ -1218,13 +1218,16 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 										m_line_width * m_par.ramming_line_width_multiplicator[old_tool],
 										layer_height_par);
 	depth = (int(length_to_extrude / width) + 1) * (m_line_width * m_par.ramming_line_width_multiplicator[old_tool] * m_par.ramming_step_multiplicator[old_tool]);
-	length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
-	length_to_extrude += volume_to_length(m_par.wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
-	length_to_extrude = std::max(length_to_extrude,0.f);
-	depth += (int(length_to_extrude / width) + 1) * m_line_width;
-	depth *= m_extra_spacing;	
+    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(m_par.wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
+    length_to_extrude = std::max(length_to_extrude,0.f);
 
-	m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth));
+	depth += (int(length_to_extrude / width) + 1) * m_line_width;
+	depth *= m_extra_spacing;
+
+	m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth,first_wipe_line));
 }
 
 
@@ -1252,31 +1255,27 @@ void WipeTowerPrusaMM::plan_tower()
 	}
 }
 
-
-void WipeTowerPrusaMM::make_wipe_tower_square()
+void WipeTowerPrusaMM::save_on_last_wipe()
 {
-	const float width = m_wipe_tower_width - 3 * m_perimeter_width;
-	const float depth = m_wipe_tower_depth - m_perimeter_width;
-	// area that we actually print into is width*depth
-	float side = sqrt(depth * width);
+    for (m_layer_info=m_plan.begin();m_layer_info<m_plan.end();++m_layer_info) {
+        set_layer(m_layer_info->z, m_layer_info->height, 0, m_layer_info->z == m_plan.front().z, m_layer_info->z == m_plan.back().z);
+        if (m_layer_info->tool_changes.size()==0)   // we have no way to save anything on an empty layer
+            continue;
 
-	m_wipe_tower_width = side + 3 * m_perimeter_width;
-	m_wipe_tower_depth = side + 2 * m_perimeter_width;
-	// For all layers, find how depth changed and update all toolchange depths
-	for (auto &lay : m_plan)
-	{
-		side = sqrt(lay.depth * width);
-		float width_ratio = width / side;
+        for (const auto &toolchange : m_layer_info->tool_changes)
+            tool_change(toolchange.new_tool, false, PURPOSE_EXTRUDE);
 
-		//lay.extra_spacing = width_ratio;
-		for (auto &tch : lay.tool_changes)
-			tch.required_depth *= width_ratio;
-	}
+        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(PURPOSE_EXTRUDE).total_extrusion_length_in_plane() : 0.f);
+        float length_to_wipe = volume_to_length(m_par.wipe_volumes[m_layer_info->tool_changes.back().old_tool][m_layer_info->tool_changes.back().new_tool],
+                              m_line_width,m_layer_info->height)  - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
 
-	plan_tower();				// propagates depth downwards again (width has changed)
-	for (auto& lay : m_plan)	// depths set, now the spacing
-		lay.extra_spacing = lay.depth / lay.toolchanges_depth();
+        length_to_wipe = std::max(length_to_wipe,0.f);
+        float depth_to_wipe = m_line_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) ) * m_extra_spacing;
 
+        //depth += (int(length_to_extrude / width) + 1) * m_line_width;
+        m_layer_info->tool_changes.back().required_depth = m_layer_info->tool_changes.back().ramming_depth + depth_to_wipe;
+    }
 }
 
 
@@ -1285,14 +1284,20 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
 void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
 {
 	if (m_plan.empty())	return;
-	else m_layer_info = m_plan.begin();
 
-	m_extra_spacing = 1.f;
+    m_extra_spacing = 1.f;
 
 	plan_tower();
+    for (int i=0;i<5;++i) {
+        save_on_last_wipe();
+        plan_tower();
+    }
+
 	if (peters_wipe_tower)
 			make_wipe_tower_square();
 
+    m_layer_info = m_plan.begin();
+
 	std::vector<WipeTower::ToolChangeResult> layer_result;
 	for (auto layer : m_plan)
 	{
@@ -1306,8 +1311,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 		if (!peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
 			m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
 
-
-
 		for (const auto &toolchange : layer.tool_changes)
 			layer_result.emplace_back(tool_change(toolchange.new_tool, false, WipeTower::PURPOSE_EXTRUDE));
 
@@ -1336,4 +1339,34 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 }
 
 
+
+
+void WipeTowerPrusaMM::make_wipe_tower_square()
+{
+	const float width = m_wipe_tower_width - 3 * m_perimeter_width;
+	const float depth = m_wipe_tower_depth - m_perimeter_width;
+	// area that we actually print into is width*depth
+	float side = sqrt(depth * width);
+
+	m_wipe_tower_width = side + 3 * m_perimeter_width;
+	m_wipe_tower_depth = side + 2 * m_perimeter_width;
+	// For all layers, find how depth changed and update all toolchange depths
+	for (auto &lay : m_plan)
+	{
+		side = sqrt(lay.depth * width);
+		float width_ratio = width / side;
+
+		//lay.extra_spacing = width_ratio;
+		for (auto &tch : lay.tool_changes)
+			tch.required_depth *= width_ratio;
+	}
+
+	plan_tower();				// propagates depth downwards again (width has changed)
+	for (auto& lay : m_plan)	// depths set, now the spacing
+		lay.extra_spacing = lay.depth / lay.toolchanges_depth();
+
+}
+
+
+
 }; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 893e4a125..16baa2b37 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -250,6 +250,9 @@ public:
 	// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
 	void make_wipe_tower_square();
 
+    // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
+    void save_on_last_wipe();
+
 	// Switch to a next layer.
 	virtual void set_layer(
 		// Print height of this layer.
@@ -427,7 +430,10 @@ private:
 			unsigned int old_tool;
 			unsigned int new_tool;
 			float required_depth;
-			ToolChange(unsigned int old,unsigned int newtool,float depth=0.f) : old_tool{old}, new_tool{newtool}, required_depth{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 z;		// z position of the layer
 		float height;	// layer height

From e1922cb2c56c61a380518aae4f273f2148198924 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 7 Mar 2018 08:55:53 +0100
Subject: [PATCH 028/118] Supressed preview on unloadingmoves

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index d6e3013e2..57135475a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -868,10 +868,12 @@ void WipeTowerPrusaMM::toolchange_Unload(
     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);
-    writer.load_move_x(turning_point,-15    , 60.f * std::hypot(xdist,15)/15 * 83 )    // fixed speed after ramming
+    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)/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(oldx         ,-12    , 60.f * std::hypot(xdist,12)/12       * m_filpar[m_current_tool].unloading_speed*0.35f );
+          .load_move_x(oldx         ,-12    , 60.f * std::hypot(xdist,12)/12       * m_filpar[m_current_tool].unloading_speed*0.35f )
+          .resume_preview();
 
 	if (new_temperature != 0) 	// Set the extruder temperature, but don't wait.
 		writer.set_extruder_temp(new_temperature, false);

From 76aa134f662ba760d01105d4dc15dbf097cf259a Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 7 Mar 2018 11:44:47 +0100
Subject: [PATCH 029/118] Ramming lines that would end unsupported are made
 longer to reach to the edge

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 79 ++++++++++++++++-----
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  2 +-
 2 files changed, 63 insertions(+), 18 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 57135475a..250bad1d2 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -790,8 +790,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	
 	writer.append("; CP TOOLCHANGE UNLOAD\n");
 	
-	const float line_width = m_line_width * m_par.ramming_line_width_multiplicator[m_current_tool]; // desired ramming line thickness
-	const float y_step = line_width * m_par.ramming_step_multiplicator[m_current_tool] * m_extra_spacing;			  // spacing between lines in mm
+	const float line_width = m_line_width * m_par.ramming_line_width_multiplicator[m_current_tool];       // desired ramming line thickness
+	const float y_step = line_width * m_par.ramming_step_multiplicator[m_current_tool] * m_extra_spacing; // spacing between lines in mm
 
 	unsigned i = 0;										// iterates through ramming_speed
 	m_left_to_right = true;								// current direction of ramming
@@ -800,14 +800,54 @@ void WipeTowerPrusaMM::toolchange_Unload(
 
 	writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
 
-	while (i < m_par.ramming_speed[m_current_tool].size())
-	{
-		const float x = volume_to_length(m_par.ramming_speed[m_current_tool][i] * 0.25f, line_width, m_layer_height);
-		const float e = m_par.ramming_speed[m_current_tool][i] * 0.25f / Filament_Area; // transform volume per sec to E move;
-		const float dist = std::min(x - e_done, remaining);		  // distance to travel for either the next 0.25s, or to the next turnaround
-		const float actual_time = dist/x * 0.25;
-		writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
-		remaining -= dist;
+    // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
+    if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end()) {
+
+        // this is y of the center of first supported line
+        float sparse_beginning_y = m_wipe_tower_pos.y
+                                    + ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth());
+
+        //debugging:
+        /* float oldx = writer.x();
+        float oldy = writer.y();
+        writer.travel(xr,sparse_beginning_y);
+        writer.extrude(xr+5,writer.y());
+        writer.travel(oldx,oldy);*/
+
+        float sum_of_depths = 0.f;
+        for (const auto& tch : m_layer_info->tool_changes) {  // let's find this toolchange
+            if (tch.old_tool == m_current_tool) {
+                sum_of_depths += tch.ramming_depth;
+                float ramming_end_y = m_wipe_tower_pos.y + sum_of_depths;
+                ramming_end_y -= (y_step/m_extra_spacing-m_line_width) / 2.f;   // center of final ramming line
+
+                // debugging:
+                /*float oldx = writer.x();
+                float oldy = writer.y();
+                writer.travel(xl,ramming_end_y);
+                writer.extrude(xl-15,writer.y());
+                writer.travel(oldx,oldy);*/
+
+                if (ramming_end_y < sparse_beginning_y) {
+                    writer.extrude(xl + tch.first_wipe_line-1.f*m_perimeter_width-0.1f,writer.y());
+                    writer.travel(xl + tch.first_wipe_line-1.f*m_perimeter_width,writer.y());
+                    remaining -= tch.first_wipe_line-1.f*m_perimeter_width;
+                }
+                break;
+            }
+            sum_of_depths += tch.required_depth;
+        }
+    }
+
+    // now the ramming itself:
+    while (i < m_par.ramming_speed[m_current_tool].size())
+    {
+        const float x = volume_to_length(m_par.ramming_speed[m_current_tool][i] * 0.25f, line_width, m_layer_height);
+        const float e = m_par.ramming_speed[m_current_tool][i] * 0.25f / Filament_Area; // transform volume per sec to E move;
+        const float dist = std::min(x - e_done, remaining);		  // distance to travel for either the next 0.25s, or to the next turnaround
+        const float actual_time = dist/x * 0.25;
+        writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
+        remaining -= dist;
 
 		if (remaining < WT_EPSILON)	{ // we reached a turning point
 			writer.travel(writer.x(), writer.y() + y_step, 7200);
@@ -1030,15 +1070,23 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 	float wipe_coeff = m_is_first_layer ? 0.5f : 1.f;
 	const float& xl = cleaning_box.ld.x;
 	const float& xr = cleaning_box.rd.x;
-	
-	// DEBUGGING: The function makes sure it always wipes at least the ordered volume, even if it means violating
-	//            the perimeter. This can later be removed and simply wipe until the end of the assigned area.
-	//			  (Variables x_to_wipe and traversed_x)
+
+
+	// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
+    //   the ordered volume, even if it means violating the box. This can later be removed and simply
+    // wipe until the end of the assigned area.
 
 	float x_to_wipe = volume_to_length(wipe_volume, m_line_width, m_layer_height);
 	float dy = m_extra_spacing*m_line_width;
 	float wipe_speed = 1600.f;
 
+    // if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway)
+    if ((m_left_to_right ? xr-writer.x() : writer.x()-xl) < 2.5f*m_perimeter_width) {
+        writer.travel((m_left_to_right ? xr-m_perimeter_width : xl+m_perimeter_width),writer.y()+dy);
+        m_left_to_right = !m_left_to_right;
+    }
+
+    // now the wiping itself:
 	for (int i = 0; true; ++i)	{
 		if (i!=0) {
 			if (wipe_speed < 1610.f) wipe_speed = 1800.f;
@@ -1067,9 +1115,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 		m_left_to_right = !m_left_to_right;
 	}
 	writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
-
-	// Wipe the nozzle
-
 }
 
 
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 16baa2b37..1b6b94308 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -448,7 +448,7 @@ private:
 	};
 
 	std::vector<WipeTowerInfo> m_plan; 	// Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
-	std::vector<WipeTowerInfo>::iterator m_layer_info;
+	std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
 
 
 	// Returns gcode for wipe tower brim

From f5cf181372116857524511ec22b8d770cbf45e50 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 7 Mar 2018 15:34:12 +0100
Subject: [PATCH 030/118] Sparse infill repositioned always to the same side

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 33 +++++++++++++--------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  1 +
 2 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 250bad1d2..fcb651423 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -622,7 +622,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.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)
+		.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_layer_parity && !peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
 		.append(";--------------------\n"
 				"; CP TOOLCHANGE START\n")
 		.comment_with_value(" toolchange #", m_num_tool_changes)
@@ -676,7 +676,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 			toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
 
 		if (last_change_in_layer) // draw perimeter line
-			writer.travel(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width), 7000)
+			writer.set_y_shift(m_y_shift)
+                .travel(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width), 7000)
 				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, 0), 3200)
 				.extrude(m_wipe_tower_pos)
 				.extrude(m_wipe_tower_pos + xy(0, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width))
@@ -801,11 +802,15 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
 
     // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
-    if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end()) {
+    if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_par.adhesion )) {
 
-        // this is y of the center of first supported line
-        float sparse_beginning_y = m_wipe_tower_pos.y
-                                    + ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth());
+        // this is y of the center of previous sparse infill border
+        float sparse_beginning_y = m_wipe_tower_pos.y;
+        if (m_layer_parity)
+            sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
+                                      - ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
+        else
+            sparse_beginning_y += (m_layer_info-1)->toolchanges_depth() + m_perimeter_width;
 
         //debugging:
         /* float oldx = writer.x();
@@ -828,9 +833,10 @@ void WipeTowerPrusaMM::toolchange_Unload(
                 writer.extrude(xl-15,writer.y());
                 writer.travel(oldx,oldy);*/
 
-                if (ramming_end_y < sparse_beginning_y) {
-                    writer.extrude(xl + tch.first_wipe_line-1.f*m_perimeter_width-0.1f,writer.y());
-                    writer.travel(xl + tch.first_wipe_line-1.f*m_perimeter_width,writer.y());
+                if ( (m_layer_parity  && ramming_end_y < sparse_beginning_y - 0.5f*m_perimeter_width  ) ||
+                     (!m_layer_parity && ramming_end_y > sparse_beginning_y + 0.5f*m_perimeter_width  )  )
+                {
+                    writer.extrude(xl + tch.first_wipe_line-1.f*m_perimeter_width,writer.y());
                     remaining -= tch.first_wipe_line-1.f*m_perimeter_width;
                 }
                 break;
@@ -1132,7 +1138,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 		.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)
+		.set_y_shift(m_y_shift - (m_layer_parity && !peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
 		.append(";--------------------\n"
 				"; CP EMPTY GRID START\n")
 		// m_num_layer_changes is incremented by set_z, so it is 1 based.
@@ -1353,8 +1359,11 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 
 		if (peters_wipe_tower)
 			m_wipe_tower_rotation_angle += 90.f;
-		else
-			m_wipe_tower_rotation_angle += 180.f;
+		else {
+            m_layer_parity = !m_layer_parity;
+            m_wipe_tower_rotation_angle += 180.f;
+        }
+
 		if (!peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
 			m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
 
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 1b6b94308..0906d334e 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -336,6 +336,7 @@ private:
 	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.
 	bool   m_is_last_layer 		= false;// Is this the last layer of this waste tower?
+    bool   m_layer_parity       = false;
 
 	// G-code generator parameters.
 	float  			m_zhop 			 = 0.5f;

From 8340a71f885eba943b45a0e08ba5182061353ed6 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 8 Mar 2018 16:44:52 +0100
Subject: [PATCH 031/118] Lots of unnecessary code removed, minor refactoring
 changes

---
 xs/src/libslic3r/GCode/WipeTower.hpp        |   8 +-
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 533 +++++++-------------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  12 +-
 xs/src/libslic3r/Print.cpp                  |   4 +-
 4 files changed, 180 insertions(+), 377 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index e193fd972..36cebeb84 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -126,17 +126,15 @@ public:
 		const std::vector<unsigned int> &tools,
 		// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
 		// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
-		bool 						last_wipe_inside_wipe_tower, 
-		// May be used by a stand alone post processor.
-		Purpose 					purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
+		bool 						last_wipe_inside_wipe_tower) = 0;
 
 	// Returns gcode for toolchange and the end position.
 	// if new_tool == -1, just unload the current filament over the wipe tower.
-	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
+	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 0;
 
 	// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
 	// Call this method only if layer_finished() is false.
-	virtual ToolChangeResult finish_layer(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
+	virtual ToolChangeResult finish_layer() = 0;
 
 	// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
 	// the wipe tower has been completely covered by the tool change extrusions,
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index fcb651423..39ccc7155 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -4,8 +4,8 @@ TODO LIST
 ---------
 
 1. cooling moves - DONE
-2. priming extrusions - DONE (fixed wiping volume so far)
-3. account for perimeter and finish_layer extrusions and subtract it from last wipe
+2. account for perimeter and finish_layer extrusions and subtract it from last wipe - DONE
+3. priming extrusions (last wipe must clear the color)
 4. Peter's wipe tower - layer's are not exactly square
 5. Peter's wipe tower - variable width for higher levels
 6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
@@ -53,7 +53,7 @@ public:
 		m_elapsed_time(0.f) {}
 
 	Writer& 			 set_initial_position(const WipeTower::xy &pos) { 
-		m_start_pos = 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);
 		m_current_pos = pos;
 		return *this;
 	}
@@ -93,7 +93,7 @@ public:
 	float                x()     const { return m_current_pos.x; }
 	float                y()     const { return m_current_pos.y; }
 	const WipeTower::xy& pos()   const { return m_current_pos; }
-	const WipeTower::xy	 start_pos_rotated() const { return WipeTower::xy(m_start_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
+	const WipeTower::xy	 start_pos_rotated() const { return m_start_pos; }
 	const WipeTower::xy  pos_rotated() const { return WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
 	float 				 elapsed_time() const { return m_elapsed_time; }
 
@@ -169,6 +169,27 @@ public:
 
 	Writer& extrude(const WipeTower::xy &dest, const float f = 0.f) 
 		{ return extrude(dest.x, dest.y, f); }
+        
+    Writer& rectangle(const WipeTower::xy& ld,float width,float height,const float f = 0.f)
+    {
+        WipeTower::xy corners[4];
+        corners[0] = ld;
+        corners[1] = WipeTower::xy(ld,width,0.f);
+        corners[2] = WipeTower::xy(ld,width,height);
+        corners[3] = WipeTower::xy(ld,0.f,height);
+        int index_of_closest = (x()-ld.x < ld.x+width-x() ? 0 : 1);
+        if (y()-ld.y > ld.y+height-x())   // closer to the top
+            index_of_closest += (index_of_closest==0 ? 3 : 1);
+        travel(corners[index_of_closest].x,y(),f);      // travel to the closest corner
+        travel(x(),corners[index_of_closest].y,f);
+        int i = index_of_closest;
+        do {
+            ++i;
+            if (i==4) i=0;
+            this->extrude(corners[i]);
+        } while (i != index_of_closest);
+        return (*this);
+    }
 
 	Writer& load(float e, float f = 0.f)
 	{
@@ -449,29 +470,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	const std::vector<unsigned int> &tools,
 	// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
 	// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
-	bool 						last_wipe_inside_wipe_tower, 
-	// May be used by a stand alone post processor.
-	Purpose 					purpose)
+	bool 						last_wipe_inside_wipe_tower)
 {
 	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
-
-	// Calculate the amount of wipe over the wipe tower brim following the prime, decrease wipe_area
-	// with the amount of material extruded over the brim.
-	// DOESN'T MAKE SENSE NOW, wipe tower dimensions are still unknown at this point
-	/*{
-		// Simulate the brim extrusions, summ the length of the extrusion.
-		float e_length = this->tool_change(0, false, PURPOSE_EXTRUDE).total_extrusion_length_in_plane();
-		// Shrink wipe_area by the amount of extrusion extruded by the finish_layer().
-		// Y stepping of the wipe extrusions.
-		float dy = m_line_width;
-		// Number of whole wipe lines, that would be extruded to wipe as much material as the finish_layer().
-		// Minimum wipe area is 5mm wide.
-		//FIXME calculate the purge_lines_width precisely.
-		float purge_lines_width = 1.3f;
-		wipe_area = std::max(5.f, m_wipe_area - float(floor(e_length / m_wipe_tower_width)) * dy - purge_lines_width);
-	}
-
-	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);*/
 	this->m_num_layer_changes 	= 0;
 	this->m_current_tool 		= tools.front();
 
@@ -502,28 +503,26 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
     sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
     writer.append(buf);
 
-	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
-			unsigned int tool = tools[idx_tool];
-			m_left_to_right = true;
-			toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials.
-			toolchange_Load(writer, cleaning_box); // Prime the tool.
-			if (idx_tool + 1 == tools.size()) {
-				// Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
-				toolchange_Wipe(writer, cleaning_box, 20.f);
-			} else {
-				// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
-				//writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
-				toolchange_Wipe(writer, cleaning_box , 5.f);
-				box_coordinates box = cleaning_box;
-				box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width);
-				toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
-				cleaning_box.translate(prime_section_width, 0.f);
-				writer.travel(cleaning_box.ld, 7200);
-			}
-		    ++ m_num_tool_changes;
-		}
-	}
+    for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
+        unsigned int tool = tools[idx_tool];
+        m_left_to_right = true;
+        toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials.
+        toolchange_Load(writer, cleaning_box); // Prime the tool.
+        if (idx_tool + 1 == tools.size()) {
+            // Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
+            toolchange_Wipe(writer, cleaning_box, 20.f);
+        } else {
+            // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
+            //writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
+            toolchange_Wipe(writer, cleaning_box , 5.f);
+            box_coordinates box = cleaning_box;
+            box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width);
+            toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
+            cleaning_box.translate(prime_section_width, 0.f);
+            writer.travel(cleaning_box.ld, 7200);
+        }
+        ++ m_num_tool_changes;
+    }
 
 	// Reset the extruder current to a normal value.
 	writer.set_extruder_trimpot(550)
@@ -534,8 +533,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	 		      ";------------------\n"
 				  "\n\n");
 
-	// Force m_idx_tool_change_in_layer to -1, so that tool_change() will know to extrude the wipe tower brim.
-	///m_idx_tool_change_in_layer = (unsigned int)(-1);
+	// so that tool_change() will know to extrude the wipe tower brim:
 	m_print_brim = true;
 
 	ToolChangeResult result;
@@ -549,48 +547,13 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	return result;
 }
 
-WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer, Purpose purpose)
+WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer)
 {
-	// Either it is the last tool unload,
-	// or there must be a nonzero wipe tower partitions available.
-//	assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0);
+	if ( m_print_brim )
+		return toolchange_Brim();
 
-	if ( m_print_brim ) {
-	///if (m_idx_tool_change_in_layer == (unsigned int)(-1)) {
-		// First layer, prime the extruder.
-		return toolchange_Brim(purpose);
-	}
 	
 	float wipe_area = 0.f;
-	{/*if (++ m_idx_tool_change_in_layer < (unsigned int)m_max_color_changes && last_in_layer) {
-		// This tool_change() call will be followed by a finish_layer() call.
-		// Try to shrink the wipe_area to save material, as less than usual wipe is required
-		// if this step is foolowed by finish_layer() extrusions wiping the same extruder.
-		for (size_t iter = 0; iter < 3; ++ iter) {
-			// Simulate the finish_layer() extrusions, summ the length of the extrusion.
-			float e_length = 0.f;
-			{
-				unsigned int old_idx_tool_change = m_idx_tool_change_in_layer;
-			    float old_wipe_start_y = m_current_wipe_start_y;
-			    m_current_wipe_start_y += wipe_area;
-				e_length = this->finish_layer(PURPOSE_EXTRUDE).total_extrusion_length_in_plane();
-				m_idx_tool_change_in_layer = old_idx_tool_change;
-				m_current_wipe_start_y = old_wipe_start_y;
-			}
-			// Shrink wipe_area by the amount of extrusion extruded by the finish_layer().
-			// Y stepping of the wipe extrusions.
-			float dy = m_line_width;
-			// Number of whole wipe lines, that would be extruded to wipe as much material as the finish_layer().
-			float num_lines_extruded = floor(e_length / m_wipe_tower_width);
-			// Minimum wipe area is 5mm wide.
-			wipe_area = m_wipe_area - num_lines_extruded * dy;
-			if (wipe_area < 5.) {
-				wipe_area = 5.;
-				break;
-			}
-		}
-	}*/}
-
 	bool last_change_in_layer = false;
 	float wipe_volume = 0.f;
 	
@@ -622,7 +585,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.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_layer_parity && !peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
+		.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
 		.append(";--------------------\n"
 				"; CP TOOLCHANGE START\n")
 		.comment_with_value(" toolchange #", m_num_tool_changes)
@@ -631,71 +594,46 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.speed_override(100);
 
 	xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
+    writer.set_initial_position(initial_position);
 
-	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		// Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower.
-		float initial_retract = ((m_filpar[m_current_tool].material == SCAFF) ? 1.f : 0.5f) * m_retract;
-		writer 	// Lift for a Z hop.
-		  	  	.z_hop(m_zhop, 7200)
-		  		// Additional retract on move to tower.
-		  		.retract(initial_retract, 3600)
-		  		// Move to a starting position, one perimeter width inside the cleaning box.
-		  		.travel(initial_position, 7200)
-		  		// Unlift for a Z hop.
-		  		.z_hop_reset(7200)
-		  		// Additional retract on move to tower.
-		  		.load(initial_retract, 3600)
-		  		.load(m_retract, 1500);
-	} else {
-		// Already at the initial position.
-		writer.set_initial_position(initial_position);
-	}
 
     // adds tag for analyzer
     char buf[32];
     sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
     writer.append(buf);
 
-	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		// Increase the extruder driver current to allow fast ramming.
-		writer.set_extruder_trimpot(750);
-		
-		// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
-		if (tool != (unsigned int)-1){ 			// This is not the last change.
-			toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
-							  m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
-			toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
-			toolchange_Load(writer, cleaning_box);			
-			toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
+    // Increase the extruder driver current to allow fast ramming.
+    writer.set_extruder_trimpot(750);
 
-			// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
-			/*box_coordinates box = cleaning_box;
-			writer.travel(box.ru, 7200)
-					.travel(box.lu);*/
-		} else
-			toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
+    // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
+    if (tool != (unsigned int)-1){ 			// This is not the last change.
+        toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
+                          m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
+        toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
+        toolchange_Load(writer, cleaning_box);
+        toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
+    } else
+        toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
 
-		if (last_change_in_layer) // draw perimeter line
-			writer.set_y_shift(m_y_shift)
-                .travel(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width), 7000)
-				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, 0), 3200)
-				.extrude(m_wipe_tower_pos)
-				.extrude(m_wipe_tower_pos + xy(0, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width))
-				.extrude(m_wipe_tower_pos + xy(peters_wipe_tower ? m_layer_info->depth + 3*m_perimeter_width : m_wipe_tower_width, peters_wipe_tower ? m_wipe_tower_depth : m_layer_info->depth + m_perimeter_width));
+    if (last_change_in_layer) {// draw perimeter line
+        writer.set_y_shift(m_y_shift);
+        if (peters_wipe_tower)
+            writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
+        else
+            writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
+    }
 
-		// Reset the extruder current to a normal value.
-		writer.set_extruder_trimpot(550)
-			  .feedrate(6000)
-			  .flush_planner_queue()
-			  .reset_extruder()
-			  .append("; CP TOOLCHANGE END\n"
-		 		      ";------------------\n"
-					  "\n\n");
+    // Reset the extruder current to a normal value.
+    writer.set_extruder_trimpot(550)
+          .feedrate(6000)
+          .flush_planner_queue()
+          .reset_extruder()
+          .append("; CP TOOLCHANGE END\n"
+                  ";------------------\n"
+                  "\n\n");
 
-	    ++ m_num_tool_changes;
-	    //m_current_wipe_start_y += wipe_area;
-		m_depth_traversed += wipe_area;
-	}
+    ++ m_num_tool_changes;
+    m_depth_traversed += wipe_area;
 
 	ToolChangeResult result;
 	result.print_z 	  	= this->m_z_pos;
@@ -708,7 +646,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	return result;
 }
 
-WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset)
+WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset)
 {
 	const box_coordinates wipeTower_box(
 		m_wipe_tower_pos,
@@ -725,46 +663,33 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
 				  "; CP WIPE TOWER FIRST LAYER BRIM START\n");
 
 	xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
-
-	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
-		// Move with Z hop.
-		writer.z_hop(m_zhop, 7200)
-			  .travel(initial_position, 6000)
-			  .z_hop_reset(7200);
-	else 
-		writer.set_initial_position(initial_position);
+	writer.set_initial_position(initial_position);
 
     // adds tag for analyzer
     char buf[32];
     sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
-    writer.append(buf);
+    writer.append(buf)
+          .extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
+                            1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
 
-	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
+    // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
+    // Extrude 4 rounds of a brim around the future wipe tower.
+    box_coordinates box(wipeTower_box);
+    box.expand(m_perimeter_width);
+    for (size_t i = 0; i < 4; ++ i) {
+        writer.travel (box.ld, 7000)
+                .extrude(box.lu, 2100).extrude(box.ru)
+                .extrude(box.rd      ).extrude(box.ld);
+        box.expand(m_perimeter_width);
+    }
 
-		writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
-								1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
+    writer.travel(wipeTower_box.ld, 7000); // Move to the front left corner.
+    writer.travel(wipeTower_box.rd) // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
+          .travel(wipeTower_box.ld);
+    writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
+                  ";-----------------------------------\n");
 
-		// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
-		// toolchange_Change(writer, int(tool), m_material[tool]);
-		
-		// Extrude 4 rounds of a brim around the future wipe tower.
-		box_coordinates box(wipeTower_box);			
-		box.expand(m_perimeter_width);
-		for (size_t i = 0; i < 4; ++ i) {
-			writer.travel (box.ld, 7000)
-					.extrude(box.lu, 2100).extrude(box.ru)
-					.extrude(box.rd      ).extrude(box.ld);
-			box.expand(m_perimeter_width);
-		}	
-
-		writer.travel(wipeTower_box.ld, 7000); // Move to the front left corner.
-		writer.travel(wipeTower_box.rd) // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
-			  .travel(wipeTower_box.ld);
-		writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
-				      ";-----------------------------------\n");
-		// Mark the brim as extruded.
-		m_print_brim = false;
-	}
+    m_print_brim = false;  // Mark the brim as extruded
 
 	ToolChangeResult result;
 	result.print_z 	  	= this->m_z_pos;
@@ -806,7 +731,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 
         // this is y of the center of previous sparse infill border
         float sparse_beginning_y = m_wipe_tower_pos.y;
-        if (m_layer_parity)
+        if (m_current_shape == SHAPE_REVERSED)
             sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
                                       - ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
         else
@@ -833,8 +758,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
                 writer.extrude(xl-15,writer.y());
                 writer.travel(oldx,oldy);*/
 
-                if ( (m_layer_parity  && ramming_end_y < sparse_beginning_y - 0.5f*m_perimeter_width  ) ||
-                     (!m_layer_parity && ramming_end_y > sparse_beginning_y + 0.5f*m_perimeter_width  )  )
+                if ( (m_current_shape == SHAPE_REVERSED   && ramming_end_y < sparse_beginning_y - 0.5f*m_perimeter_width  ) ||
+                     (m_current_shape == SHAPE_NORMAL && ramming_end_y > sparse_beginning_y + 0.5f*m_perimeter_width  )  )
                 {
                     writer.extrude(xl + tch.first_wipe_line-1.f*m_perimeter_width,writer.y());
                     remaining -= tch.first_wipe_line-1.f*m_perimeter_width;
@@ -868,47 +793,6 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	}
 	WipeTower::xy end_of_ramming(writer.x(),writer.y());
 
-// Alex's old ramming:
-{
-  /*switch (current_material)
-	{
-	case ABS:
-   		// ramming          start                    end                  y increment     amount feedrate
-		writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width,     y_step * 0.2f, 0,  1.2f  * e, 4000)
-			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.2f, e0, 1.6f  * e, 4600)
-			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, e0, 1.8f  * e, 5000)
-			  .ram(xr - m_perimeter_width * 2, xl + m_perimeter_width * 2, y_step * 1.2f, e0, 1.8f  * e, 5000);
-		break;
-	case PVA:
-		// Used for the PrimaSelect PVA
-		writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width,     y_step * 0.2f, 0,  1.75f * e, 4000)
-			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.5f, 0,  1.75f * e, 4500)
-			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.5f, 0,  1.75f * e, 4800)
-			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.5f, 0,  1.75f * e, 5000);
-		break;
-	case SCAFF:
-		writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width,     y_step * 2.f,  0,  1.75f * e, 4000)
-			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 3.f,  0,  2.34f * e, 4600)
-			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 3.f,  0,  2.63f * e, 5200);
-		break;
-	default:
-		// PLA, PLA/PHA and others
-		// Used for the Verbatim BVOH, PET, NGEN, co-polyesters
-		writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width,     y_step * 0.2f, 0,  1.60f * e, 4000)
-			  .ram(xr - m_perimeter_width,     xl + m_perimeter_width,     y_step * 1.2f, e0, 1.65f * e, 4600)
-			  .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, e0, 1.74f * e, 5200);
-	}*/
-}
-	// Pull the filament end into a cooling tube (Alex)
-	//writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
-
-    // Pull the filament end to the BEGINNING of the cooling tube
-    /*float unloading_feedrate = 60.f * m_filpar[m_current_tool].unloading_speed;
-    writer.retract(15, 5000)                              // just after ramming - always the same speed
-          .retract(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42, unloading_feedrate)
-          .retract(15, unloading_feedrate*0.55f)
-          .retract(12, unloading_feedrate*0.35f);*/
-
     // 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
@@ -957,42 +841,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
     // 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);
 
-	writer.travel(writer.x(), writer.y() - y_step,2400);
-
-	// Alex's old cooling:
-	{
-		// In case the current print head position is closer to the left edge, reverse the direction.
-		/*if (std::abs(writer.x() - xl) < std::abs(writer.x() - xr))
-		std::swap(xl, xr);
-		// Horizontal cooling moves will be performed at the following Y coordinate:
-		writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
-		  .suppress_preview();
-		switch (current_material)
-		{
-		case PVA:
-			writer.cool(xl, xr, 3, -5, 1600)
-				.cool(xl, xr, 5, -5, 2000)
-				.cool(xl, xr, 5, -5, 2200)
-				.cool(xl, xr, 5, -5, 2400)
-				.cool(xl, xr, 5, -5, 2400)
-				.cool(xl, xr, 5, -3, 2400);
-			break;
-		case SCAFF:
-			writer.cool(xl, xr, 3, -5, 1600)
-				.cool(xl, xr, 5, -5, 2000)
-				.cool(xl, xr, 5, -5, 2200)
-				.cool(xl, xr, 5, -5, 2200)
-				.cool(xl, xr, 5, -3, 2400);
-			break;
-		default:
-			writer.cool(xl, xr, 3, -5, 1600)
-				.cool(xl, xr, 5, -5, 2000)
-				.cool(xl, xr, 5, -5, 2400)
-				.cool(xl, xr, 5, -3, 2400);
-		}*/
-}
 	// this is to align ramming and future loading extrusions, so the future y-steps can be uniform from the start:
-	writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_line_width) / 2.f, 2400.f);
+	writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_line_width) / 2.f + m_perimeter_width, 2400.f);
 
 	writer.resume_preview()
 		  .flush_planner_queue();
@@ -1036,6 +886,7 @@ void WipeTowerPrusaMM::toolchange_Load(
     float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
 	writer.append("; CP TOOLCHANGE LOAD\n")
 		  .suppress_preview()
+          .travel(writer.x(),writer.y() + m_perimeter_width)                                   // So that we move in free space
 		  .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(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f)  // Slowing down
@@ -1043,20 +894,6 @@ void WipeTowerPrusaMM::toolchange_Load(
 		  .travel(oldx,oldy)
 		  .resume_preview();
 
-// Alex's old loading extrusions - this has been moved to toolchange_Wipe(...)
-{
-	// Extrude first five lines (just three lines if colorInit is set).
-	/*writer.extrude(xr, writer.y(), 1600);
-	bool   colorInit = false;
-	size_t pass = colorInit ? 1 : 2;
-	float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_line_width;
-	for (int i = 0; i < pass; ++ i) {
-		writer.travel (xr, writer.y() + dy, 7200);
-		writer.extrude(xl, writer.y(), 		2200);
-		writer.travel (xl, writer.y() + dy, 7200);
-	 	writer.extrude(xr, writer.y(), 		2200);
-	}*/	
-}
 	// Reset the extruder current to the normal value.
 	writer.set_extruder_trimpot(550);
 }
@@ -1091,6 +928,8 @@ void WipeTowerPrusaMM::toolchange_Wipe(
         writer.travel((m_left_to_right ? xr-m_perimeter_width : xl+m_perimeter_width),writer.y()+dy);
         m_left_to_right = !m_left_to_right;
     }
+    
+    writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling & loading was done a fraction down the road
 
     // now the wiping itself:
 	for (int i = 0; true; ++i)	{
@@ -1126,7 +965,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 
 
 
-WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
+WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 {
 	// This should only be called if the layer is not finished yet.
 	// Otherwise the caller would likely travel to the wipe tower in vain.
@@ -1138,7 +977,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 		.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_layer_parity && !peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
+		.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
 		.append(";--------------------\n"
 				"; CP EMPTY GRID START\n")
 		// m_num_layer_changes is incremented by set_z, so it is 1 based.
@@ -1150,93 +989,65 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
 	box_coordinates fill_box(m_wipe_tower_pos + xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
 							 m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
 
-	if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		if ( m_layer_info->tool_changes.size() == 0) {
-			// There were no tool changes at all in this layer.
-			writer.retract(m_retract * 1.5f, 3600)
-				  // Jump with retract to fill_box.ld + a random shift in +x.
-				  .z_hop(m_zhop, 7200)
-				  .travel(fill_box.ld + xy(5.f + 15.f * float(rand()) / RAND_MAX, 0.f), 7000)
-				  .z_hop_reset(7200)
-				  // Prime the extruder.
-				  .load_move_x(fill_box.ld.x, m_retract * 1.5f, 3600);
-		} else {
-			// Otherwise the extruder is already over the wipe tower.
-		}
-	} else {
-		// The print head is inside the wipe tower. Rather move to the start of the following extrusion.
-		writer.set_initial_position(fill_box.lu);
-	}
+    // The print head is inside the wipe tower. Rather move to the start of the following extrusion.
+    writer.set_initial_position(fill_box.lu);
 
-	if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
-		box_coordinates box = fill_box;
-		for (int i=0;i<2;++i) {
-			if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer
-				if (i==0) box.expand(m_perimeter_width);
-				else box.expand(-m_perimeter_width);
-			}
-			else i=2;	// only draw the inner perimeter
-
-			writer.travel(box.lu,7200)
-			    .extrude(box.ru, 2400 * speed_factor)
-			    .extrude(box.rd)
-			    .extrude(box.ld)
-			    .extrude(box.lu);
-		}
-
-		if (m_is_first_layer && m_par.adhesion) {
-			// Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
-			box.expand(-m_perimeter_width/2.f);
-			unsigned nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
-			float step   = (box.lu.y - box.ld.y) / nsteps;
-			writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f));
-			if (nsteps >= 0)
-				for (size_t i = 0; i < nsteps; ++i)	{
-					writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step);
-					writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y());
-					writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
-					writer.extrude(box.ld.x + m_perimeter_width / 2.f, writer.y());
-				}
-				writer.travel(box.rd.x-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
-		} else {
-			// Extrude a sparse infill to support the material to be printed above.
-
-			const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width);
-			if (dy > m_perimeter_width)
-			{
-				// Extrude an inverse U at the left of the region.
-				writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f))
-					  .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
-
-				const float left = fill_box.lu.x+2*m_perimeter_width;
-				const float right = fill_box.ru.x - 2 * m_perimeter_width;
-				const int n = 1+(right-left)/(m_par.bridging);
-				const float dx = (right-left)/n;
-				for (int i=1;i<=n;++i) {
-					float x=left+dx*i;
-					writer.travel(x,writer.y());
-					writer.extrude(x,i%2 ? fill_box.rd.y : fill_box.ru.y);					
-				}
-				writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
-			}
-		}
+	box_coordinates box = fill_box;
+    for (int i=0;i<2;++i) {
+        if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer
+            if (i==0) box.expand(m_perimeter_width);
+            else box.expand(-m_perimeter_width);
+        }
+        else i=2;	// only draw the inner perimeter, outer has been already drawn by tool_change(...)
+        writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y);
 /*
-		// if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
-		if (true)
-	       	// Wipe along the front side of the current wiping box.
-			// Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
-			writer.travel(fill_box.ld + xy(  m_perimeter_width, m_perimeter_width / 2), 7200)
-			  	  .travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2));
-		else
-			writer.feedrate(7200);
-*/
-		writer.append("; CP EMPTY GRID END\n"
-				      ";------------------\n\n\n\n\n\n\n");
+        writer.travel(box.lu,7200)
+            .extrude(box.ru, 2400 * speed_factor)
+            .extrude(box.rd)
+            .extrude(box.ld)
+            .extrude(box.lu);*/
+    }
+
+    if (m_is_first_layer && m_par.adhesion) {
+        // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
+        box.expand(-m_perimeter_width/2.f);
+        unsigned nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
+        float step   = (box.lu.y - box.ld.y) / nsteps;
+        writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f));
+        if (nsteps >= 0)
+            for (size_t i = 0; i < nsteps; ++i)	{
+                writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step);
+                writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y());
+                writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
+                writer.extrude(box.ld.x + m_perimeter_width / 2.f, writer.y());
+            }
+            writer.travel(box.rd.x-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
+    }
+    else {  // Extrude a sparse infill to support the material to be printed above.
+        const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width);
+        if (dy > m_perimeter_width)
+        {
+            // Extrude an inverse U at the left of the region.
+            writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f))
+                  .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
+
+            const float left = fill_box.lu.x+2*m_perimeter_width;
+            const float right = fill_box.ru.x - 2 * m_perimeter_width;
+            const int n = 1+(right-left)/(m_par.bridging);
+            const float dx = (right-left)/n;
+            for (int i=1;i<=n;++i) {
+                float x=left+dx*i;
+                writer.travel(x,writer.y());
+                writer.extrude(x,i%2 ? fill_box.rd.y : fill_box.ru.y);
+            }
+            writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
+        }
+    }
+    writer.append("; CP EMPTY GRID END\n"
+                  ";------------------\n\n\n\n\n\n\n");
+
+    m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
 
-		// Indicate that this wipe tower layer is fully covered.
-		m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
-	    ///m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;		
-	}
 	ToolChangeResult result;
 	result.print_z 	  	= this->m_z_pos;
 	result.layer_height = this->m_layer_height;
@@ -1316,10 +1127,10 @@ void WipeTowerPrusaMM::save_on_last_wipe()
             continue;
 
         for (const auto &toolchange : m_layer_info->tool_changes)
-            tool_change(toolchange.new_tool, false, PURPOSE_EXTRUDE);
+            tool_change(toolchange.new_tool, false);
 
         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(PURPOSE_EXTRUDE).total_extrusion_length_in_plane() : 0.f);
+        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(m_par.wipe_volumes[m_layer_info->tool_changes.back().old_tool][m_layer_info->tool_changes.back().new_tool],
                               m_line_width,m_layer_info->height)  - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
 
@@ -1359,19 +1170,17 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 
 		if (peters_wipe_tower)
 			m_wipe_tower_rotation_angle += 90.f;
-		else {
-            m_layer_parity = !m_layer_parity;
+		else
             m_wipe_tower_rotation_angle += 180.f;
-        }
 
 		if (!peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
 			m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
 
 		for (const auto &toolchange : layer.tool_changes)
-			layer_result.emplace_back(tool_change(toolchange.new_tool, false, WipeTower::PURPOSE_EXTRUDE));
+			layer_result.emplace_back(tool_change(toolchange.new_tool, false));
 
 		if (! layer_finished()) {
-			layer_result.emplace_back(finish_layer(WipeTower::PURPOSE_EXTRUDE));
+			layer_result.emplace_back(finish_layer());
 			if (layer_result.size() > 1) {
 				// Merge the two last tool changes into one.
 				WipeTower::ToolChangeResult &tc1 = layer_result[layer_result.size() - 2];
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 0906d334e..3a1b332fd 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -269,8 +269,6 @@ public:
 		m_z_pos 				= print_z;
 		m_layer_height			= layer_height;
 		m_is_first_layer 		= is_first_layer;
-		// Start counting the color changes from zero. Special case -1: extrude a brim first.
-		///m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
 		m_print_brim = is_first_layer;
 		m_depth_traversed  = 0.f; // to make room for perimeter line
 		m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
@@ -299,17 +297,15 @@ public:
 		const std::vector<unsigned int> &tools,
 		// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
 		// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
-		bool 						last_wipe_inside_wipe_tower, 
-		// May be used by a stand alone post processor.
-		Purpose 					purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE);
+		bool 						last_wipe_inside_wipe_tower);
 
 	// Returns gcode for a toolchange and a final print head position.
 	// On the first layer, extrude a brim around the future wipe tower first.
-	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer, Purpose purpose);
+	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
 
 	// Fill the unfilled space with a zig-zag.
 	// Call this method only if layer_finished() is false.
-	virtual ToolChangeResult finish_layer(Purpose purpose);
+	virtual ToolChangeResult finish_layer();
 
 	// Is the current layer finished?
 	virtual bool 			 layer_finished() const {
@@ -455,7 +451,7 @@ private:
 	// Returns gcode for wipe tower brim
 	// sideOnly			-- set to false -- experimental, draw brim on sides of wipe tower
 	// offset			-- set to 0		-- experimental, offset to replace brim in front / rear of wipe tower
-	ToolChangeResult toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
+	ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
 
 	void toolchange_Unload(
 		PrusaMultiMaterial::Writer &writer,
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 447aec750..5affad297 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1052,7 +1052,7 @@ void Print::_make_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, WipeTower::PURPOSE_EXTRUDE));
+        wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full));
 
 
     // Lets go through the wipe tower layers and determine pairs of extruder changes for each
@@ -1146,7 +1146,7 @@ void Print::_make_wipe_tower()
         wipe_tower.set_layer(float(m_tool_ordering.back().print_z), float(layer_height), 0, false, true);
     }
     m_wipe_tower_final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
-		wipe_tower.tool_change((unsigned int)-1, false, WipeTower::PURPOSE_EXTRUDE));
+		wipe_tower.tool_change((unsigned int)-1, false));
 }
 
 std::string Print::output_filename()

From 6e39f61198e237a85a43b08df6c3aaa6e7fa775e Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 9 Mar 2018 12:40:39 +0100
Subject: [PATCH 032/118] Priming extrusions, nozzle-wipes when leaving tower,
 removed few unnecessary moves

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 71 +++++++++++++--------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  2 +-
 2 files changed, 44 insertions(+), 29 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 39ccc7155..671819286 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -72,8 +72,11 @@ public:
 	Writer&				 set_rotation(WipeTower::xy& pos, float width, float depth, float angle)
 		{ m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
 
-	Writer&				 set_y_shift(float shift)
-		{ m_y_shift = shift; return (*this); }
+	Writer&				 set_y_shift(float shift) {
+        m_current_pos.y += shift-m_y_shift;
+        m_y_shift = shift;
+        return (*this);
+    }
 
 	// 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
@@ -481,8 +484,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
     // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
 //	box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
 	//FIXME: set the width properly
-	const float prime_section_width = 60.f;
-	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 15.f);
+	const float prime_section_width = 55.f;
+	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 100.f);
 
 	PrusaMultiMaterial::Writer writer;
 	writer.set_extrusion_flow(m_extrusion_flow)
@@ -510,11 +513,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
         toolchange_Load(writer, cleaning_box); // Prime the tool.
         if (idx_tool + 1 == tools.size()) {
             // Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
-            toolchange_Wipe(writer, cleaning_box, 20.f);
+            toolchange_Wipe(writer, cleaning_box, m_par.wipe_volumes[tools[idx_tool-1]][tool]);
         } else {
             // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
             //writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
-            toolchange_Wipe(writer, cleaning_box , 5.f);
+            toolchange_Wipe(writer, cleaning_box , 20.f);
             box_coordinates box = cleaning_box;
             box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width);
             toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
@@ -611,20 +614,28 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
                           m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
         toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
         toolchange_Load(writer, cleaning_box);
-        toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
+        writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
+        toolchange_Wipe(writer, cleaning_box, wipe_volume);     // Wipe the newly loaded filament until the end of the assigned wipe area.
     } else
         toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
 
+    ++ m_num_tool_changes;
+    m_depth_traversed += wipe_area;
+
     if (last_change_in_layer) {// draw perimeter line
         writer.set_y_shift(m_y_shift);
         if (peters_wipe_tower)
             writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
-        else
+        else {
+            writer.wait(2.f);
             writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
+            if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
+                writer.travel(m_wipe_tower_pos.x + (writer.x()>m_wipe_tower_pos.x + EPSILON ? 0.f : m_wipe_tower_width), writer.y());
+            }
+        }
     }
 
-    // Reset the extruder current to a normal value.
-    writer.set_extruder_trimpot(550)
+    writer.set_extruder_trimpot(550)    // Reset the extruder current to a normal value.
           .feedrate(6000)
           .flush_planner_queue()
           .reset_extruder()
@@ -632,9 +643,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
                   ";------------------\n"
                   "\n\n");
 
-    ++ m_num_tool_changes;
-    m_depth_traversed += wipe_area;
-
 	ToolChangeResult result;
 	result.print_z 	  	= this->m_z_pos;
 	result.layer_height = this->m_layer_height;
@@ -841,7 +849,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
     // 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);
 
-	// this is to align ramming and future loading extrusions, so the future y-steps can be uniform from the start:
+	// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
+    // the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
 	writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_line_width) / 2.f + m_perimeter_width, 2400.f);
 
 	writer.resume_preview()
@@ -877,7 +886,6 @@ void WipeTowerPrusaMM::toolchange_Load(
 	float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f;
 	float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f;
 	float oldx = writer.x();	// the nozzle is in place to do the first wiping moves, we will remember the position
-	float oldy = writer.y();
 
     // 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
@@ -886,12 +894,10 @@ void WipeTowerPrusaMM::toolchange_Load(
     float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
 	writer.append("; CP TOOLCHANGE LOAD\n")
 		  .suppress_preview()
-          .travel(writer.x(),writer.y() + m_perimeter_width)                                   // So that we move in free space
 		  .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(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
-		  .travel(oldx,oldy)
 		  .resume_preview();
 
 	// Reset the extruder current to the normal value.
@@ -929,7 +935,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
         m_left_to_right = !m_left_to_right;
     }
     
-    writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling & loading was done a fraction down the road
 
     // now the wiping itself:
 	for (int i = 0; true; ++i)	{
@@ -959,6 +964,14 @@ void WipeTowerPrusaMM::toolchange_Wipe(
 		writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5*m_perimeter_width, writer.y() + dy);
 		m_left_to_right = !m_left_to_right;
 	}
+
+    // this is neither priming nor not the last toolchange on this layer - we are going back to the model - wipe the nozzle
+    if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
+        m_left_to_right = !m_left_to_right;
+        writer.travel(writer.x(), writer.y() - dy)
+        .travel(m_wipe_tower_pos.x + (m_left_to_right ? m_wipe_tower_width : 0.f), writer.y());
+    }
+
 	writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
 }
 
@@ -989,8 +1002,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 	box_coordinates fill_box(m_wipe_tower_pos + xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
 							 m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
 
-    // The print head is inside the wipe tower. Rather move to the start of the following extrusion.
-    writer.set_initial_position(fill_box.lu);
+    if (m_left_to_right) // so there is never a diagonal travel
+        writer.set_initial_position(fill_box.ru);
+    else
+        writer.set_initial_position(fill_box.lu);
 
 	box_coordinates box = fill_box;
     for (int i=0;i<2;++i) {
@@ -1000,14 +1015,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
         }
         else i=2;	// only draw the inner perimeter, outer has been already drawn by tool_change(...)
         writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y);
-/*
-        writer.travel(box.lu,7200)
-            .extrude(box.ru, 2400 * speed_factor)
-            .extrude(box.rd)
-            .extrude(box.ld)
-            .extrude(box.lu);*/
     }
 
+    // we are in one of the corners, travel to ld along the perimeter:
+    if (writer.x() > fill_box.ld.x+EPSILON) writer.travel(fill_box.ld.x,writer.y());
+    if (writer.y() > fill_box.ld.y+EPSILON) writer.travel(writer.x(),fill_box.ld.y);
+
     if (m_is_first_layer && m_par.adhesion) {
         // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
         box.expand(-m_perimeter_width/2.f);
@@ -1025,14 +1038,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
     }
     else {  // Extrude a sparse infill to support the material to be printed above.
         const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width);
+        const float left = fill_box.lu.x+2*m_perimeter_width;
+        const float right = fill_box.ru.x - 2 * m_perimeter_width;
         if (dy > m_perimeter_width)
         {
             // Extrude an inverse U at the left of the region.
             writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f))
                   .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
 
-            const float left = fill_box.lu.x+2*m_perimeter_width;
-            const float right = fill_box.ru.x - 2 * m_perimeter_width;
             const int n = 1+(right-left)/(m_par.bridging);
             const float dx = (right-left)/n;
             for (int i=1;i<=n;++i) {
@@ -1042,6 +1055,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
             }
             writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
         }
+        else
+            writer.travel(right,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
     }
     writer.append("; CP EMPTY GRID END\n"
                   ";------------------\n\n\n\n\n\n\n");
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 3a1b332fd..3182c8b8e 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -471,7 +471,7 @@ private:
 	void toolchange_Wipe(
 		PrusaMultiMaterial::Writer &writer,
 		const box_coordinates  &cleaning_box,
-		float wipe_volume);	
+		float wipe_volume);
 };
 
 

From b5fd4ddb8c50908e5754df8f49d698eb16f0fd2f Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 12 Mar 2018 08:35:35 +0100
Subject: [PATCH 033/118] Removed an unnecessary wait

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 671819286..624b48903 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -627,7 +627,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
         if (peters_wipe_tower)
             writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
         else {
-            writer.wait(2.f);
             writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
             if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
                 writer.travel(m_wipe_tower_pos.x + (writer.x()>m_wipe_tower_pos.x + EPSILON ? 0.f : m_wipe_tower_width), writer.y());

From 27ab8ff4e1fc9efdc5ca61ff1ae70cdfde16b4e8 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 12 Mar 2018 15:41:25 +0100
Subject: [PATCH 034/118] Configuration layer changes (cooling_time, bridging,
 adhesion moved from dedicated dialog to filament/print settings)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 12 ++---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 42 ++++++++---------
 xs/src/libslic3r/Print.cpp                  |  5 ++
 xs/src/libslic3r/PrintConfig.cpp            | 23 ++++++++++
 xs/src/libslic3r/PrintConfig.hpp            |  6 +++
 xs/src/slic3r/GUI/Preset.cpp                |  6 +--
 xs/src/slic3r/GUI/Tab.cpp                   | 23 ++++++++++
 xs/src/slic3r/GUI/WipeTowerDialog.cpp       | 51 ++-------------------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp       | 33 -------------
 9 files changed, 91 insertions(+), 110 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 624b48903..b94950af1 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -734,7 +734,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
 
     // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
-    if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_par.adhesion )) {
+    if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
 
         // this is y of the center of previous sparse infill border
         float sparse_beginning_y = m_wipe_tower_pos.y;
@@ -821,8 +821,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	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,(m_par.cooling_time[m_current_tool]-14)/3);
-	float time = m_par.cooling_time[m_current_tool] / float(N);
+	const unsigned int N = 4 + std::max(0,(m_filpar[m_current_tool].cooling_time-14)/3);
+	float time = m_filpar[m_current_tool].cooling_time / float(N);
 
 	i = 0;
 	while (i<N) {
@@ -832,7 +832,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 		// 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_par.cooling_time[m_current_tool] / float(N);
+			time = m_filpar[m_current_tool].cooling_time / float(N);
 		}
 		else
 			time -= e_dist / speed; // subtract time this part will really take
@@ -1020,7 +1020,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
     if (writer.x() > fill_box.ld.x+EPSILON) writer.travel(fill_box.ld.x,writer.y());
     if (writer.y() > fill_box.ld.y+EPSILON) writer.travel(writer.x(),fill_box.ld.y);
 
-    if (m_is_first_layer && m_par.adhesion) {
+    if (m_is_first_layer && m_adhesion) {
         // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
         box.expand(-m_perimeter_width/2.f);
         unsigned nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
@@ -1045,7 +1045,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
             writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f))
                   .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
 
-            const int n = 1+(right-left)/(m_par.bridging);
+            const int n = 1+(right-left)/(m_bridging);
             const float dx = (right-left)/n;
             for (int i=1;i<=n;++i) {
                 float x=left+dx*i;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 3182c8b8e..3c2f82ffd 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -68,13 +68,12 @@ struct WipeTowerParameters {
     WipeTowerParameters() {  }           // create new empty object
     WipeTowerParameters(const std::string& init_data) { // create object and initialize from std::string
         std::istringstream in(init_data);               // validation of input is left to the caller
-        in >> bridging >> adhesion >> sampling;
+        in >> sampling;
         for (std::vector<float> vect{} ; in >> vect ;) {  // until we get to fail state ("**")...
-            if (vect.size()>=3) {
-                cooling_time.push_back(vect[0]);
-                ramming_line_width_multiplicator.push_back(vect[1]);
-                ramming_step_multiplicator.push_back(vect[2]);
-                vect.erase(vect.begin(),vect.begin()+3);
+            if (vect.size()>=2) {
+                ramming_line_width_multiplicator.push_back(vect[0]);
+                ramming_step_multiplicator.push_back(vect[1]);
+                vect.erase(vect.begin(),vect.begin()+2);
             }
             else vect.clear(); // something's not right, we will restore defaults anyway
             ramming_speed.push_back(vect);
@@ -102,16 +101,16 @@ struct WipeTowerParameters {
         for (unsigned int i=0;i<vect.size();++i)
             if (i%2==1)
                 filament_wipe_volumes.push_back(std::make_pair(vect[i-1],vect[i]));
-
+        
         if (!validate())    // in case we did not parse the input right
             set_defaults();
     }
 
     std::string to_string() {
         std::ostringstream out;
-        out << bridging << " " << int(adhesion) << " " << sampling << "\n";
-        for (unsigned extruder=0;extruder<cooling_time.size();++extruder) {
-            out << "\n" << cooling_time[extruder] << " "  << ramming_line_width_multiplicator[extruder] << " " 
+        out << sampling << "\n";
+        for (unsigned extruder=0;extruder<ramming_step_multiplicator.size();++extruder) {
+            out << "\n" << ramming_line_width_multiplicator[extruder] << " " 
                 << ramming_step_multiplicator[extruder] << " " << ramming_speed[extruder]  << "*"
                 << ramming_buttons[extruder] << "*";
         }
@@ -124,7 +123,7 @@ struct WipeTowerParameters {
     }
 
     bool validate() const {     // basic check for validity to distinguish most dramatic failures
-        const unsigned int num = cooling_time.size();
+        const unsigned int num = ramming_step_multiplicator.size();
         if ( num < 1 || ramming_line_width_multiplicator.size()!=num || ramming_step_multiplicator.size()!=num ||
              ramming_buttons.size()!=num || wipe_volumes.size()!=num ||
              filament_wipe_volumes.size()!=num)
@@ -135,10 +134,7 @@ struct WipeTowerParameters {
         return true;
     }
     void set_defaults() {
-        bridging = 10;
-        adhesion = true;
         sampling = 0.25f;
-        cooling_time = {15,15,15,15};
         ramming_line_width_multiplicator = {1.5f, 1.5f, 1.5f, 1.5f};
         ramming_step_multiplicator = {1.1f, 1.1f, 1.1f, 1.1f};
         ramming_speed.clear();
@@ -153,11 +149,8 @@ struct WipeTowerParameters {
                         { 60.f, 60.f, 60.f,  0.f}};
         filament_wipe_volumes = {{30.f,30.f},{30.f,30.f},{30.f,30.f},{30.f,30.f}};
     }
-
-    int bridging = 0.f;
-    bool adhesion  = false;
+    
     float sampling = 0.25f; // this does not quite work yet, keep it fixed to 0.25f
-    std::vector<int> cooling_time;
     std::vector<float> ramming_line_width_multiplicator;
     std::vector<float> ramming_step_multiplicator;
     std::vector<std::vector<float>> ramming_speed;
@@ -192,7 +185,7 @@ 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 wipe_area, float rotation_angle, float cooling_tube_retraction,
-                     float cooling_tube_length, float parking_pos_retraction, std::string& parameters,
+                     float cooling_tube_length, float parking_pos_retraction, float bridging, bool adhesion, std::string& parameters,
                      unsigned int initial_tool) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
@@ -205,8 +198,11 @@ public:
         m_cooling_tube_length(cooling_tube_length),
         m_parking_pos_retraction(parking_pos_retraction),
 		m_current_tool(initial_tool),
-        m_par(parameters)
+        m_par(parameters) 
  	{
+        m_bridging = bridging;
+        m_adhesion = adhesion;
+        
 		for (size_t i = 0; i < 4; ++ i) {
 			// Extruder specific parameters.
 			m_filpar[i].material = PLA;
@@ -226,7 +222,7 @@ 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 unloading_speed, float delay, int cooling_time)
 	{
         m_filpar[idx].material = material;
         m_filpar[idx].temperature = temp;
@@ -234,6 +230,7 @@ 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;
 	}
 
 
@@ -340,6 +337,8 @@ private:
     float           m_cooling_tube_retraction   = 0.f;
     float           m_cooling_tube_length       = 0.f;
     float           m_parking_pos_retraction    = 0.f;
+    float           m_bridging                  = 0.f;
+    bool            m_adhesion                  = true;
 
 	float m_line_width = Nozzle_Diameter * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
 	float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
@@ -352,6 +351,7 @@ private:
         float           loading_speed;
         float           unloading_speed;
         float           delay;
+        int             cooling_time;
     };
 
 	// Extruder specific parameters.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 5affad297..95c57339a 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -188,6 +188,7 @@ 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 == "gcode_flavor"
             || opt_key == "single_extruder_multi_material"
             || opt_key == "spiral_vase"
@@ -199,6 +200,8 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "wipe_tower_width"
             || opt_key == "wipe_tower_per_color_wipe"
             || opt_key == "wipe_tower_rotation_angle"
+            || opt_key == "wipe_tower_bridging"
+            || opt_key == "wipe_tower_adhesion"
             || opt_key == "z_offset") {
             steps.emplace_back(psWipeTower);
         } else if (
@@ -1030,6 +1033,7 @@ void Print::_make_wipe_tower()
         float(this->config.wipe_tower_width.value), float(this->config.wipe_tower_per_color_wipe.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), bool(this->config.wipe_tower_adhesion),
         this->config.wipe_tower_advanced.value,m_tool_ordering.first_extruder());
     
     //wipe_tower.set_retract();
@@ -1044,6 +1048,7 @@ void Print::_make_wipe_tower()
             this->config.first_layer_temperature.get_at(i),
             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_toolchange_delay.get_at(i));
 
     // When printing the first layer's wipe tower, the first extruder is expected to be active and primed.
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index f31e5de3b..b0b1c4e99 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -469,6 +469,15 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "filament-toolchange-delay=f@";
     def->min = 0;
     def->default_value = new ConfigOptionFloats { 0. };
+    
+    def = this->add("filament_cooling_time", coInts);
+    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->min = 0;
+    def->default_value = new ConfigOptionInts { 14 };
 
     def = this->add("filament_diameter", coFloats);
     def->label = L("Diameter");
@@ -1800,6 +1809,20 @@ PrintConfigDef::PrintConfigDef()
     def->sidetext = L("degrees");
     def->cli = "wipe-tower-rotation-angle=f";
     def->default_value = new ConfigOptionFloat(0.);
+    
+    def = this->add("wipe_tower_bridging", coFloat);
+    def->label = L("Maximal bridging distance");
+    def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
+    def->sidetext = L("mm");
+    def->cli = "wipe-tower-bridging=f";
+    def->default_value = new ConfigOptionFloat(10.);
+    
+    def = this->add("wipe_tower_adhesion", coBool);
+    def->label = L("Increase first layer adhesion");
+    def->tooltip = L("This prevents using sparse infill on the first layer, if it would be "
+                   "normally applied. Dense infill is used instead. ");
+    def->cli = "wipe-tower_adhesion!";
+    def->default_value = new ConfigOptionBool(true);
 
     def = this->add("xy_size_compensation", coFloat);
     def->label = L("XY Size Compensation");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 06aa343b8..501a8ac36 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -470,6 +470,7 @@ public:
     ConfigOptionFloats              filament_loading_speed;
     ConfigOptionFloats              filament_unloading_speed;
     ConfigOptionFloats              filament_toolchange_delay;
+    ConfigOptionInts                filament_cooling_time;
     ConfigOptionBool                gcode_comments;
     ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
     ConfigOptionString              layer_gcode;
@@ -525,6 +526,7 @@ protected:
         OPT_PTR(filament_loading_speed);
         OPT_PTR(filament_unloading_speed);
         OPT_PTR(filament_toolchange_delay);
+        OPT_PTR(filament_cooling_time);
         OPT_PTR(gcode_comments);
         OPT_PTR(gcode_flavor);
         OPT_PTR(layer_gcode);
@@ -624,6 +626,8 @@ public:
     ConfigOptionFloat               wipe_tower_width;
     ConfigOptionFloat               wipe_tower_per_color_wipe;
     ConfigOptionFloat               wipe_tower_rotation_angle;
+    ConfigOptionFloat               wipe_tower_bridging;
+    ConfigOptionBool                wipe_tower_adhesion;
     ConfigOptionFloat               z_offset;
     
 protected:
@@ -690,6 +694,8 @@ protected:
         OPT_PTR(wipe_tower_width);
         OPT_PTR(wipe_tower_per_color_wipe);
         OPT_PTR(wipe_tower_rotation_angle);
+        OPT_PTR(wipe_tower_bridging);
+        OPT_PTR(wipe_tower_adhesion);
         OPT_PTR(z_offset);
     }
 };
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index a8b6a514c..92bfef416 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -199,8 +199,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_advanced", "wipe_tower", "wipe_tower_x",
-        "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "compatible_printers",
-        "compatible_printers_condition"
+        "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_adhesion", "wipe_tower_bridging",
+        "compatible_printers", "compatible_printers_condition"
 
     };
     return s_opts;
@@ -211,7 +211,7 @@ 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",
-        "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling",
+        "filament_cooling_time", "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"
     };
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 177fccc59..ea80e2ece 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -458,6 +458,8 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_width");
 		optgroup->append_single_option_line("wipe_tower_per_color_wipe");
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
+        optgroup->append_single_option_line("wipe_tower_bridging");
+        optgroup->append_single_option_line("wipe_tower_adhesion");
         line = { _(L("Advanced")), "" };
         line.widget = [this](wxWindow* parent){
 			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _(L("Advanced settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
@@ -880,6 +882,27 @@ 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");
+        line = { _(L("Ramming")), "" };
+        line.widget = [this](wxWindow* parent){
+			auto ramming_dialog = new wxButton(parent, wxID_ANY, _(L("Advanced settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+			auto sizer = new wxBoxSizer(wxHORIZONTAL);
+			sizer->Add(ramming_dialog);
+			/*m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
+			{
+                std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
+                std::cout << "dialog init: " << init_data << std::endl;
+				WipeTowerDialog dlg(this,init_data); // dlg lives on stack, no need to call Destroy
+
+				if (dlg.ShowModal() == wxID_OK) {
+                    load_key_value("wipe_tower_advanced", dlg.GetValue());
+                    std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
+                }
+			}));*/
+			return sizer;
+		};
+		optgroup->append_line(line);
+
 
         page = add_options_page(_(L("Custom G-code")), "cog.png");
 		optgroup = page->new_optgroup(_(L("Start G-code")), 0);
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 323749b0a..1891a401c 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -3,13 +3,8 @@
 
 // Human-readable output of Parameters structure
 std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par) {
-    str << "bridging: " << par.bridging << "\n";
-    str << "adhesion: " << par.adhesion << "\n";
     str << "sampling: " << par.sampling << "\n"; 
    
-    str << "cooling times: ";
-    for (const auto& a : par.cooling_time) str << a << " ";
-    
     str << "line widths: ";
     for (const auto& a : par.ramming_line_width_multiplicator) str << a << " ";
     
@@ -116,30 +111,6 @@ void RammingPanel::line_parameters_changed() {
 
 
 
-
-CoolingPanel::CoolingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
-: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
-{
-    new wxStaticText(this,wxID_ANY,wxString("Time (in seconds) reserved for cooling after unload:"),wxPoint(220,50) ,wxSize(400,25),wxALIGN_LEFT);
-    for (int i=0;i<4;++i) {
-        new wxStaticText(this,wxID_ANY,wxString("Filament #")<<i+1<<": ",wxPoint(300,105+30*i) ,wxSize(150,25),wxALIGN_LEFT);
-        m_widget_edits.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(400,100+30*i),wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,30,15));
-    }        
-    for (unsigned int i=0;i<p.cooling_time.size();++i) {
-        if (i>=m_widget_edits.size())
-            break;  // so we don't initialize non-existent widget
-        m_widget_edits[i]->SetValue(p.cooling_time[i]);
-    }
-}
-
-void CoolingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
-    p.cooling_time.clear();
-    for (int i=0;i<4;++i)
-        p.cooling_time.push_back(m_widget_edits[i]->GetValue());
-}
-
-
-
 WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
 : wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
 {
@@ -208,20 +179,6 @@ void WipingPanel::fill_in_matrix() {
 
 
 
-GeneralPanel::GeneralPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p) : wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED) {
-    new wxStaticText(this,wxID_ANY,wxString("Maximum bridging over sparse infill (mm):"),wxPoint(100,105) ,wxSize(280,25),wxALIGN_LEFT);
-    m_widget_bridge = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(380,100),wxSize(50,25),wxALIGN_RIGHT|wxSP_ARROW_KEYS,1,50,10);
-    m_widget_adhesion = new wxCheckBox(this,wxID_ANY,"Increased adhesion of first layer",wxPoint(100,150),wxSize(330,25),wxALIGN_RIGHT);
-    m_widget_bridge->SetValue(p.bridging);
-    m_widget_adhesion->SetValue(p.adhesion);
-}
-
-void GeneralPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
-    p.bridging = m_widget_bridge->GetValue();
-    p.adhesion = m_widget_adhesion->GetValue();        
-}
-  
-
 
 
 
@@ -238,13 +195,13 @@ WipeTowerDialog::WipeTowerDialog(wxWindow* parent,const std::string& init_data)
             
     wxNotebook* notebook = new wxNotebook(this,wxID_ANY,wxPoint(0,0),wxSize(800,450));
     
-    m_panel_general = new GeneralPanel(notebook,parameters);
+    //m_panel_general = new GeneralPanel(notebook,parameters);
     m_panel_ramming = new RammingPanel(notebook,parameters);
-    m_panel_cooling = new CoolingPanel(notebook,parameters);
+    //m_panel_cooling = new CoolingPanel(notebook,parameters);
     m_panel_wiping  = new WipingPanel(notebook,parameters);
-    notebook->AddPage(m_panel_general,"General");
+    //notebook->AddPage(m_panel_general,"General");
     notebook->AddPage(m_panel_ramming,"Ramming");
-    notebook->AddPage(m_panel_cooling,"Cooling");
+    //notebook->AddPage(m_panel_cooling,"Cooling");
     notebook->AddPage(m_panel_wiping,"Wiping");
     this->Show();
 
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index 452886bc3..8a1c88be8 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -40,23 +40,6 @@ private:
 
 
 
-
-
-
-class CoolingPanel : public wxPanel {
-public:
-    CoolingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
-    void fill_parameters(Slic3r::WipeTowerParameters& p);
-    
-private:
-    std::vector<wxSpinCtrl*> m_widget_edits;
-};
-
-
-
-
-
-
 class WipingPanel : public wxPanel {
 public:
     WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
@@ -74,18 +57,6 @@ private:
 
 
 
-class GeneralPanel : public wxPanel {
-public:
-    GeneralPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
-    void fill_parameters(Slic3r::WipeTowerParameters& p);
-    
-private:
-    wxSpinCtrl* m_widget_bridge;
-    wxCheckBox* m_widget_adhesion;
-};
-
-
-
 
 class WipeTowerDialog : public wxDialog {
 public:
@@ -96,17 +67,13 @@ public:
     
 private:
     std::string m_file_name="config_wipe_tower";
-    GeneralPanel* m_panel_general = nullptr;
     RammingPanel* m_panel_ramming = nullptr;
-    CoolingPanel* m_panel_cooling = nullptr;
     WipingPanel*  m_panel_wiping  = nullptr;
     std::string m_output_data = "";
             
     std::string read_dialog_values() {
         Slic3r::WipeTowerParameters p;
-        m_panel_general->fill_parameters(p);
         m_panel_ramming->fill_parameters(p);
-        m_panel_cooling->fill_parameters(p);
         m_panel_wiping ->fill_parameters(p);
         return p.to_string();
     }

From cf5dfb9673a37203b36b9f56cf135d4d021d7f08 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 13 Mar 2018 15:54:29 +0100
Subject: [PATCH 035/118] Started to move ramming dialog into filament settings

---
 xs/src/slic3r/GUI/RammingChart.cpp    | 98 ++++++++++++---------------
 xs/src/slic3r/GUI/RammingChart.hpp    | 61 +++++++----------
 xs/src/slic3r/GUI/Tab.cpp             | 24 +++----
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 88 ++++++++++++------------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp | 23 ++++---
 5 files changed, 135 insertions(+), 159 deletions(-)

diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
index 3b535aacf..e07b95839 100644
--- a/xs/src/slic3r/GUI/RammingChart.cpp
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -10,41 +10,41 @@ void Chart::draw(wxDC& dc) {
     dc.SetBrush(*wxWHITE_BRUSH);
     dc.DrawRectangle(m_rect);
     
-    if (visible_area->m_width < 0.499) {
+    if (visible_area.m_width < 0.499) {
         dc.DrawText("NO RAMMING AT ALL",wxPoint(m_rect.GetLeft()+m_rect.GetWidth()/2-50,m_rect.GetBottom()-m_rect.GetHeight()/2));
         return;
     }
     
     
-    if (!m_line_to_draw->empty()) {
-        for (unsigned int i=0;i<m_line_to_draw->size()-2;++i) {
-            int color = 510*((m_rect.GetBottom()-(*m_line_to_draw)[i])/double(m_rect.GetHeight()));
+    if (!m_line_to_draw.empty()) {
+        for (unsigned int i=0;i<m_line_to_draw.size()-2;++i) {
+            int color = 510*((m_rect.GetBottom()-(m_line_to_draw)[i])/double(m_rect.GetHeight()));
             dc.SetPen( wxPen( wxColor(std::min(255,color),255-std::max(color-255,0),0), 1 ) );
-            dc.DrawLine(m_rect.GetLeft()+1+i,(*m_line_to_draw)[i],m_rect.GetLeft()+1+i,m_rect.GetBottom());        
+            dc.DrawLine(m_rect.GetLeft()+1+i,(m_line_to_draw)[i],m_rect.GetLeft()+1+i,m_rect.GetBottom());        
         }
         dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
-        for (unsigned int i=0;i<m_line_to_draw->size()-2;++i) {
+        for (unsigned int i=0;i<m_line_to_draw.size()-2;++i) {
             if (splines)
-                dc.DrawLine(m_rect.GetLeft()+i,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i+1]);        
+                dc.DrawLine(m_rect.GetLeft()+i,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i+1]);
             else {
-                dc.DrawLine(m_rect.GetLeft()+i,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i]);        
-                dc.DrawLine(m_rect.GetLeft()+i+1,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i+1]);        
-            }                
+                dc.DrawLine(m_rect.GetLeft()+i,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i]);
+                dc.DrawLine(m_rect.GetLeft()+i+1,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i+1]);
+            }
         }
     }
     
     // draw draggable buttons
     dc.SetBrush(*wxBLUE_BRUSH);
     dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
-    for (auto& button : m_buttons[m_current_extruder])
+    for (auto& button : m_buttons)
         //dc.DrawRectangle(math_to_screen(button.get_pos())-wxPoint(side/2.,side/2.), wxSize(side,side));
         dc.DrawCircle(math_to_screen(button.get_pos()),side/2.);
         //dc.DrawRectangle(math_to_screen(button.get_pos()-wxPoint2DDouble(0.125,0))-wxPoint(0,5),wxSize(50,10));
 
     // draw x-axis:
     float last_mark = -10000;
-    for (float math_x=int(visible_area->m_x*10)/10 ; math_x <= (visible_area->m_x+visible_area->m_width) ; math_x+=0.1) {
-        int x = math_to_screen(wxPoint2DDouble(math_x,visible_area->m_y)).x;
+    for (float math_x=int(visible_area.m_x*10)/10 ; math_x <= (visible_area.m_x+visible_area.m_width) ; math_x+=0.1) {
+        int x = math_to_screen(wxPoint2DDouble(math_x,visible_area.m_y)).x;
         int y = m_rect.GetBottom(); 
         if (x-last_mark < 50) continue;
         dc.DrawLine(x,y+3,x,y-3);
@@ -54,8 +54,8 @@ void Chart::draw(wxDC& dc) {
     
     // draw y-axis:
     last_mark=10000;
-    for (int math_y=visible_area->m_y ; math_y <= (visible_area->m_y+visible_area->m_height) ; math_y+=1) {
-        int y = math_to_screen(wxPoint2DDouble(visible_area->m_x,math_y)).y;
+    for (int math_y=visible_area.m_y ; math_y <= (visible_area.m_y+visible_area.m_height) ; math_y+=1) {
+        int y = math_to_screen(wxPoint2DDouble(visible_area.m_x,math_y)).y;
         int x = m_rect.GetLeft();
         if (last_mark-y < 50) continue;    
         dc.DrawLine(x-3,y,x+3,y);
@@ -72,8 +72,8 @@ void Chart::mouse_right_button_clicked(wxMouseEvent& event) {
         return;
     wxPoint point = event.GetPosition();
     int button_index = which_button_is_clicked(point);
-    if (button_index != -1 && m_buttons[m_current_extruder].size()>2) {
-        m_buttons[m_current_extruder].erase(m_buttons[m_current_extruder].begin()+button_index);
+    if (button_index != -1 && m_buttons.size()>2) {
+        m_buttons.erase(m_buttons.begin()+button_index);
         recalculate_line();
     }
 }
@@ -84,7 +84,7 @@ void Chart::mouse_clicked(wxMouseEvent& event) {
     wxPoint point = event.GetPosition();
     int button_index = which_button_is_clicked(point);
     if ( button_index != -1) {
-        m_dragged = &m_buttons[m_current_extruder][button_index];
+        m_dragged = &m_buttons[button_index];
         m_previous_mouse = point;            
     }
 }
@@ -102,7 +102,7 @@ void Chart::mouse_moved(wxMouseEvent& event) {
     }    
     int delta_x = pos.x - m_previous_mouse.x;
     int delta_y = pos.y - m_previous_mouse.y;
-    m_dragged->move(fixed_x?0:double(delta_x)/m_rect.GetWidth() * visible_area->m_width,-double(delta_y)/m_rect.GetHeight() * visible_area->m_height); 
+    m_dragged->move(fixed_x?0:double(delta_x)/m_rect.GetWidth() * visible_area.m_width,-double(delta_y)/m_rect.GetHeight() * visible_area.m_height); 
     m_previous_mouse = pos;
     recalculate_line();
 }
@@ -115,8 +115,8 @@ void Chart::mouse_double_clicked(wxMouseEvent& event) {
     wxPoint point = event.GetPosition();
     if (!m_rect.Contains(point))     // the click is outside the chart
         return;
-    m_buttons[m_current_extruder].push_back(screen_to_math(point));
-    std::sort(m_buttons[m_current_extruder].begin(),m_buttons[m_current_extruder].end());
+    m_buttons.push_back(screen_to_math(point));
+    std::sort(m_buttons.begin(),m_buttons.end());
     recalculate_line();
     return;
 }
@@ -126,7 +126,7 @@ void Chart::mouse_double_clicked(wxMouseEvent& event) {
 
 void Chart::recalculate_line() {
     std::vector<wxPoint> points;
-    for (auto& but : m_buttons[m_current_extruder]) {
+    for (auto& but : m_buttons) {
         points.push_back(wxPoint(math_to_screen(but.get_pos())));
         if (points.size()>1 && points.back().x==points[points.size()-2].x) points.pop_back();
         if (points.size()>1 && points.back().x > m_rect.GetRight()) {
@@ -136,7 +136,7 @@ void Chart::recalculate_line() {
     }
     std::sort(points.begin(),points.end(),[](wxPoint& a,wxPoint& b) { return a.x < b.x; });
     
-    m_line_to_draw->clear();
+    m_line_to_draw.clear();
     m_total_volume = 0.f;
    
    
@@ -205,19 +205,19 @@ void Chart::recalculate_line() {
                     y = (rhs[i-1]*pow(points[i].x-x,3)+rhs[i]*pow(x-points[i-1].x,3)) / (6*h[i]) +
                         (points[i-1].y-rhs[i-1]*h[i]*h[i]/6.f) * (points[i].x-x)/h[i] +
                         (points[i].y  -rhs[i]  *h[i]*h[i]/6.f) * (x-points[i-1].x)/h[i];
-            m_line_to_draw->push_back(y);
+            m_line_to_draw.push_back(y);
         }
         else {            
             float x_math = screen_to_math(wxPoint(x,0)).m_x;
-            if (i+2<=points.size() && m_buttons[m_current_extruder][i+1].get_pos().m_x-0.125 < x_math)
+            if (i+2<=points.size() && m_buttons[i+1].get_pos().m_x-0.125 < x_math)
                 ++i;
-            m_line_to_draw->push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[m_current_extruder][i].get_pos().m_y)).y);
+            m_line_to_draw.push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[i].get_pos().m_y)).y);
         }
             
                     
-        m_line_to_draw->back() = std::max(m_line_to_draw->back(), m_rect.GetTop()-1);
-        m_line_to_draw->back() = std::min(m_line_to_draw->back(), m_rect.GetBottom()-1);
-        m_total_volume += (m_rect.GetBottom() - m_line_to_draw->back()) * (visible_area->m_width / m_rect.GetWidth()) * (visible_area->m_height / m_rect.GetHeight());
+        m_line_to_draw.back() = std::max(m_line_to_draw.back(), m_rect.GetTop()-1);
+        m_line_to_draw.back() = std::min(m_line_to_draw.back(), m_rect.GetBottom()-1);
+        m_total_volume += (m_rect.GetBottom() - m_line_to_draw.back()) * (visible_area.m_width / m_rect.GetWidth()) * (visible_area.m_height / m_rect.GetHeight());
     }
     
     wxPostEvent(this->GetParent(), wxCommandEvent(EVT_WIPE_TOWER_CHART_CHANGED));
@@ -226,36 +226,26 @@ void Chart::recalculate_line() {
 
 
 
-std::vector<std::vector<float>> Chart::get_ramming_speeds(float sampling) const {
-    std::vector<std::vector<float>> speeds_out;
-    for (unsigned int extruder_id = 0;extruder_id<m_buttons.size();++extruder_id) { // repeat for each extruder
-        std::vector<float> this_extruder;
-        const int number_of_samples = std::round( visible_areas[extruder_id].m_width / sampling);
-        if (number_of_samples>0) {
-            const int dx = (m_lines_to_draw[extruder_id].size()-1) / number_of_samples;
-            for (int j=0;j<number_of_samples;++j) {
-                float left =  screen_to_math(wxPoint(0,m_lines_to_draw[extruder_id][j*dx])).m_y;
-                float right = screen_to_math(wxPoint(0,m_lines_to_draw[extruder_id][(j+1)*dx])).m_y;
-                this_extruder.push_back((left+right)/2.f);            
-            }
-        }
-        /*else
-            this_extruder.push_back(0.f); // so it does not stay empty*/
-        speeds_out.push_back(std::move(this_extruder));            
-    }
+std::vector<float> Chart::get_ramming_speed(float sampling) const {
+    std::vector<float> speeds_out;
     
+    const int number_of_samples = std::round( visible_area.m_width / sampling);
+    if (number_of_samples>0) {
+        const int dx = (m_line_to_draw.size()-1) / number_of_samples;
+        for (int j=0;j<number_of_samples;++j) {
+            float left =  screen_to_math(wxPoint(0,m_line_to_draw[j*dx])).m_y;
+            float right = screen_to_math(wxPoint(0,m_line_to_draw[(j+1)*dx])).m_y;
+            speeds_out.push_back((left+right)/2.f);            
+        }
+    }
     return speeds_out;
 }
 
 
-std::vector<std::vector<std::pair<float,float>>> Chart::get_buttons() const {
-    std::vector<std::vector<std::pair<float, float>>> buttons_out;
-    for (const auto& ext : m_buttons) {
-        std::vector<std::pair<float,float>> this_extruder;
-        for (const auto& button : ext)
-            this_extruder.push_back(std::make_pair(button.get_pos().m_x,button.get_pos().m_y));            
-        buttons_out.push_back(std::move(this_extruder));            
-    }
+std::vector<std::pair<float,float>> Chart::get_buttons() const {
+    std::vector<std::pair<float, float>> buttons_out;
+    for (const auto& button : m_buttons)
+        buttons_out.push_back(std::make_pair(button.get_pos().m_x,button.get_pos().m_y));            
     return buttons_out;
 }
     
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
index 8274b96e6..520b5808e 100644
--- a/xs/src/slic3r/GUI/RammingChart.hpp
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -13,39 +13,28 @@ wxDECLARE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
 class Chart : public wxWindow {
         
 public:
-    Chart(wxWindow* parent, wxRect rect,const std::vector<std::vector<std::pair<float,float>>>& initial_buttons,std::vector<std::vector<float>> ramming_speed, float sampling) :
+    Chart(wxWindow* parent, wxRect rect,const std::vector<std::pair<float,float>>& initial_buttons,std::vector<float> ramming_speed, float sampling) :
         wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize())
     {
         m_rect=wxRect(wxPoint(30,0),rect.GetSize()-wxSize(30,30));
-        for (int i=0;i<4;++i) {
-            visible_areas.push_back(wxRect2DDouble(0.0, 0.0, sampling*ramming_speed[i].size(), 20.));
-            m_buttons.push_back(std::vector<ButtonToDrag>());
-            m_lines_to_draw.push_back(std::vector<int>());
-            if (initial_buttons.size()>0)
-                for (const auto& pair : initial_buttons[i])
-                    m_buttons.back().push_back(wxPoint2DDouble(pair.first,pair.second));
-            set_extruder(i); // to calculate all interpolating splines
-        }
-        set_extruder(0);
-    }
-    void set_extruder(unsigned ext) {
-        if (ext>=4) return;
-        m_current_extruder = ext;
-        visible_area = &(visible_areas[ext]);
-        m_line_to_draw = &(m_lines_to_draw[ext]);
+        visible_area = wxRect2DDouble(0.0, 0.0, sampling*ramming_speed.size(), 20.);
+        m_buttons.clear();
+        if (initial_buttons.size()>0)
+            for (const auto& pair : initial_buttons)
+                m_buttons.push_back(wxPoint2DDouble(pair.first,pair.second));
         recalculate_line();
     }
     void set_xy_range(float x,float y) {
         x = int(x/0.5) * 0.5;
-        if (x>=0) visible_area->SetRight(x);
-        if (y>=0) visible_area->SetBottom(y);
+        if (x>=0) visible_area.SetRight(x);
+        if (y>=0) visible_area.SetBottom(y);
         recalculate_line();
     }
     float get_volume() const { return m_total_volume; }
-    float get_time()   const { return visible_area->m_width; }
-    std::vector<std::vector<float>> get_ramming_speeds(float sampling) const; //returns sampled ramming speed for all extruders
-    std::vector<std::vector<std::pair<float,float>>> get_buttons() const; // returns buttons position for all extruders
-   
+    float get_time()   const { return visible_area.m_width; }
+    
+    std::vector<float> get_ramming_speed(float sampling) const; //returns sampled ramming speed
+    std::vector<std::pair<float,float>> get_buttons() const; // returns buttons position   
     
     void draw(wxDC& dc);
     
@@ -80,8 +69,8 @@ private:
     
     wxPoint math_to_screen(const wxPoint2DDouble& math) const {
         wxPoint screen;
-        screen.x = (math.m_x-visible_area->m_x) * (m_rect.GetWidth()  / visible_area->m_width  );
-        screen.y = (math.m_y-visible_area->m_y) * (m_rect.GetHeight() / visible_area->m_height );
+        screen.x = (math.m_x-visible_area.m_x) * (m_rect.GetWidth()  / visible_area.m_width  );
+        screen.y = (math.m_y-visible_area.m_y) * (m_rect.GetHeight() / visible_area.m_height );
         screen.y *= -1;
         screen += m_rect.GetLeftBottom();            
         return screen;
@@ -90,16 +79,16 @@ private:
         wxPoint2DDouble math = screen;
         math -= m_rect.GetLeftBottom();
         math.m_y *= -1;
-        math.m_x *= visible_area->m_width   / m_rect.GetWidth();    // scales to [0;1]x[0,1]
-        math.m_y *= visible_area->m_height / m_rect.GetHeight();
-        return (math+visible_area->GetLeftTop());
+        math.m_x *= visible_area.m_width   / m_rect.GetWidth();    // scales to [0;1]x[0,1]
+        math.m_y *= visible_area.m_height / m_rect.GetHeight();
+        return (math+visible_area.GetLeftTop());
     }
         
     int which_button_is_clicked(const wxPoint& point) const {
         if (!m_rect.Contains(point))
             return -1;
-        for (unsigned int i=0;i<m_buttons[m_current_extruder].size();++i) {
-            wxRect rect(math_to_screen(m_buttons[m_current_extruder][i].get_pos())-wxPoint(side/2.,side/2.),wxSize(side,side)); // bounding rectangle of this button
+        for (unsigned int i=0;i<m_buttons.size();++i) {
+            wxRect rect(math_to_screen(m_buttons[i].get_pos())-wxPoint(side/2.,side/2.),wxSize(side,side)); // bounding rectangle of this button
             if ( rect.Contains(point) )
                 return i;
         }
@@ -111,17 +100,13 @@ private:
     void recalculate_volume();
      
     
-    unsigned int m_current_extruder = 0;
     wxRect m_rect;                  // rectangle on screen the chart is mapped into (screen coordinates)
     wxPoint m_previous_mouse;        
-    std::vector< std::vector<ButtonToDrag> > m_buttons;
-    std::vector< std::vector<int> > m_lines_to_draw;
-    std::vector< wxRect2DDouble > visible_areas;
-    wxRect2DDouble* visible_area = nullptr;
-    std::vector<int>* m_line_to_draw = nullptr;
+    std::vector<ButtonToDrag> m_buttons;
+    std::vector<int> m_line_to_draw;
+    wxRect2DDouble visible_area;
     ButtonToDrag* m_dragged = nullptr;
-    float m_total_volume = 0.f;
-    
+    float m_total_volume = 0.f;  
     
 };
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index ea80e2ece..58d714fc7 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -796,7 +796,7 @@ void TabPrint::update()
 
 	bool have_wipe_tower = m_config->opt_bool("wipe_tower");
 	vec_enable.resize(0);
-	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle"};
+	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_bridging"};
 	for (auto el : vec_enable)
 		get_field(el)->toggle(have_wipe_tower);
     m_wipe_tower_btn->Enable(have_wipe_tower);
@@ -885,20 +885,18 @@ void TabFilament::build()
         optgroup->append_single_option_line("filament_cooling_time");
         line = { _(L("Ramming")), "" };
         line.widget = [this](wxWindow* parent){
-			auto ramming_dialog = new wxButton(parent, wxID_ANY, _(L("Advanced settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
-			auto sizer = new wxBoxSizer(wxHORIZONTAL);
-			sizer->Add(ramming_dialog);
-			/*m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
+			auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+            auto sizer = new wxBoxSizer(wxHORIZONTAL);
+			sizer->Add(ramming_dialog_btn);
+            
+            ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
 			{
-                std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
-                std::cout << "dialog init: " << init_data << std::endl;
-				WipeTowerDialog dlg(this,init_data); // dlg lives on stack, no need to call Destroy
-
-				if (dlg.ShowModal() == wxID_OK) {
-                    load_key_value("wipe_tower_advanced", dlg.GetValue());
-                    std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
+                //std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
+                RammingDialog dlg(this,std::string());
+                if (dlg.ShowModal() == wxID_OK) {
+                    //load_key_value("wipe_tower_advanced", dlg.GetValue());
                 }
-			}));*/
+			}));
 			return sizer;
 		};
 		optgroup->append_line(line);
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 1891a401c..8bf51e4e5 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -40,12 +40,37 @@ std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par) {
         
     return str;
 }
+
+
+RammingDialog::RammingDialog(wxWindow* parent,const std::string& init_data)
+: wxDialog(parent, -1,  wxT("Ramming customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+{
+    this->Centre();
+    m_panel_ramming  = new RammingPanel(this,Slic3r::WipeTowerParameters(std::string()));
+    m_panel_ramming->Show(true);
+    this->Show();
+
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(m_panel_ramming, 1, wxEXPAND);
+    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
+    SetSizer(main_sizer);
+    SetMinSize(GetSize());
+    main_sizer->SetSizeHints(this);
+  
+    this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
     
+    this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
+       // m_output_data=read_dialog_values();
+        EndModal(wxID_OK);
+        },wxID_OK);
+}
+
+
 
 
 
 RammingPanel::RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
-: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
 {
     new wxStaticText(this,wxID_ANY,wxString("Total ramming time (s):"),     wxPoint(500,105),      wxSize(200,25),wxALIGN_LEFT);
     m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,       wxPoint(700,100),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0.,5.0,3.,0.5);        
@@ -55,56 +80,40 @@ RammingPanel::RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p
     m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,       wxPoint(700,200),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        
     new wxStaticText(this,wxID_ANY,wxString("Ramming line spacing (%):"),   wxPoint(500,235),      wxSize(200,25),wxALIGN_LEFT);
     m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,     wxPoint(700,230),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        
-    new wxStaticText(this,wxID_ANY,wxString("Extruder #:"),                 wxPoint(500,12),       wxSize(200,25),wxALIGN_LEFT);
     
-    wxArrayString choices;
-    for (unsigned int i=0;i<p.ramming_line_width_multiplicator.size();++i) {            // for all extruders
-        choices.Add(wxString("")<<i+1);
-        m_ramming_line_width_multiplicators.push_back(p.ramming_line_width_multiplicator[i]*100);
-        m_ramming_step_multiplicators.push_back(p.ramming_step_multiplicator[i]*100);
-    }
-    m_widget_extruder = new wxChoice(this,wxID_ANY,wxPoint(580,5),wxSize(50,27),choices);
     
-    m_chart = new Chart(this,wxRect(10,10,480,360),p.ramming_buttons,p.ramming_speed,p.sampling);
+    m_chart = new Chart(this,wxRect(10,10,480,360),p.ramming_buttons[0],p.ramming_speed[0],p.sampling);
     
-    m_chart->set_extruder(0);
     m_widget_time->SetValue(m_chart->get_time());
     m_widget_time->SetDigits(2);
     m_widget_volume->SetValue(m_chart->get_volume());
     m_widget_volume->Disable();
-    m_widget_extruder->SetSelection(0);
-    extruder_selection_changed(); // tell everyone to redraw
+    m_widget_ramming_line_width_multiplicator->SetValue(m_ramming_line_width_multiplicator);
+    m_widget_ramming_step_multiplicator->SetValue(m_ramming_step_multiplicator);        
     
     m_widget_ramming_step_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
     m_widget_ramming_line_width_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
-    m_widget_extruder->Bind(wxEVT_CHOICE,[this](wxCommandEvent&) { extruder_selection_changed(); });
     m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(m_widget_time->GetValue(),-1);});
     m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){});      // do nothing - prevents the user to change the value
     m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){});    // do nothing - prevents the user to change the value   
-    Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} );        
+    Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} );
+    Refresh(this);
 }
 
+
 void RammingPanel::fill_parameters(Slic3r::WipeTowerParameters& p)
 {
     if (!m_chart) return;
-    p.ramming_buttons = m_chart->get_buttons();
-    p.ramming_speed   = m_chart->get_ramming_speeds(p.sampling);
-    for (unsigned int i=0;i<m_ramming_line_width_multiplicators.size();++i) {  // we assume m_ramming_line_width_multiplicators.size() == m_ramming_step_multiplicators.size()         
-        p.ramming_line_width_multiplicator.push_back(m_ramming_line_width_multiplicators[i]/100.f);
-        p.ramming_step_multiplicator.push_back(m_ramming_step_multiplicators[i]/100.f);
-    }
-}
-    
-void RammingPanel::extruder_selection_changed() {
-    m_current_extruder = m_widget_extruder->GetSelection();
-    m_chart->set_extruder(m_current_extruder);  // tell our chart to redraw
-    m_widget_ramming_line_width_multiplicator  ->SetValue(m_ramming_line_width_multiplicators[m_current_extruder]);
-    m_widget_ramming_step_multiplicator->SetValue(m_ramming_step_multiplicators[m_current_extruder]);        
+    p.ramming_buttons[0] = m_chart->get_buttons();
+    p.ramming_speed[0]   = m_chart->get_ramming_speed(p.sampling);
+    p.ramming_line_width_multiplicator.push_back(m_ramming_line_width_multiplicator/100.f);
+    p.ramming_step_multiplicator.push_back(m_ramming_step_multiplicator/100.f);
 }
 
+
 void RammingPanel::line_parameters_changed() {
-    m_ramming_line_width_multiplicators[m_current_extruder]=m_widget_ramming_line_width_multiplicator->GetValue();
-    m_ramming_step_multiplicators[m_current_extruder]=m_widget_ramming_step_multiplicator->GetValue();
+    m_ramming_line_width_multiplicator = m_widget_ramming_line_width_multiplicator->GetValue();
+    m_ramming_step_multiplicator = m_widget_ramming_step_multiplicator->GetValue();
 }
 
 
@@ -112,7 +121,7 @@ void RammingPanel::line_parameters_changed() {
 
 
 WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
-: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
 {
     const int N = 4; // number of extruders
     new wxStaticText(this,wxID_ANY,wxString("Volume to wipe when the filament is being"),wxPoint(40,55) ,wxSize(500,25));
@@ -141,6 +150,7 @@ WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
     }
     
     m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){fill_in_matrix();});
+    Refresh(this);
 }
 
 void WipingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
@@ -183,7 +193,7 @@ void WipingPanel::fill_in_matrix() {
 
 
 WipeTowerDialog::WipeTowerDialog(wxWindow* parent,const std::string& init_data)
-: wxDialog(parent, -1,  wxT("Wipe tower advanced settings"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+: wxDialog(parent, -1,  wxT("Wiping customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
     this->Centre();
             
@@ -192,21 +202,11 @@ WipeTowerDialog::WipeTowerDialog(wxWindow* parent,const std::string& init_data)
         wxMessageDialog(this,"Wipe tower parameters not parsed correctly!\nRestoring default settings.","Error",wxICON_ERROR);
         parameters.set_defaults();
     }
-            
-    wxNotebook* notebook = new wxNotebook(this,wxID_ANY,wxPoint(0,0),wxSize(800,450));
-    
-    //m_panel_general = new GeneralPanel(notebook,parameters);
-    m_panel_ramming = new RammingPanel(notebook,parameters);
-    //m_panel_cooling = new CoolingPanel(notebook,parameters);
-    m_panel_wiping  = new WipingPanel(notebook,parameters);
-    //notebook->AddPage(m_panel_general,"General");
-    notebook->AddPage(m_panel_ramming,"Ramming");
-    //notebook->AddPage(m_panel_cooling,"Cooling");
-    notebook->AddPage(m_panel_wiping,"Wiping");
+    m_panel_wiping  = new WipingPanel(this,parameters);
     this->Show();
 
     auto main_sizer = new wxBoxSizer(wxVERTICAL);
-    main_sizer->Add(notebook, 1, wxEXPAND);
+    main_sizer->Add(m_panel_wiping, 1, wxEXPAND);
     main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
     SetSizer(main_sizer);
     SetMinSize(GetSize());
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index 8a1c88be8..f9a9f9524 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -15,10 +15,10 @@
 
 // Human-readable output of Parameters structure
 std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par);
-    
 
 class RammingPanel : public wxPanel {
 public:
+    RammingPanel(wxWindow* parent);
     RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
     void fill_parameters(Slic3r::WipeTowerParameters& p);
 
@@ -28,17 +28,22 @@ private:
     wxSpinCtrl* m_widget_ramming_line_width_multiplicator = nullptr;
     wxSpinCtrl* m_widget_ramming_step_multiplicator = nullptr;
     wxSpinCtrlDouble* m_widget_time = nullptr;
-    wxChoice* m_widget_extruder = nullptr;
-    std::vector<int> m_ramming_step_multiplicators;    
-    std::vector<int> m_ramming_line_width_multiplicators;
-    int m_current_extruder = 0;     // zero-based index
-    
-    void extruder_selection_changed();
-    
+    int m_ramming_step_multiplicator;
+    int m_ramming_line_width_multiplicator;
+      
     void line_parameters_changed();
 };
 
 
+class RammingDialog : public wxDialog {
+public:
+    RammingDialog(wxWindow* parent,const std::string& init_data);    
+private:
+    RammingPanel* m_panel_ramming = nullptr;
+};
+
+
+
 
 class WipingPanel : public wxPanel {
 public:
@@ -67,13 +72,11 @@ public:
     
 private:
     std::string m_file_name="config_wipe_tower";
-    RammingPanel* m_panel_ramming = nullptr;
     WipingPanel*  m_panel_wiping  = nullptr;
     std::string m_output_data = "";
             
     std::string read_dialog_values() {
         Slic3r::WipeTowerParameters p;
-        m_panel_ramming->fill_parameters(p);
         m_panel_wiping ->fill_parameters(p);
         return p.to_string();
     }

From 5f28b89ae02a873080a229f76debd2e5f406ef0e Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 14 Mar 2018 11:54:11 +0100
Subject: [PATCH 036/118] Added a project specific config to the PresetBundle
 class. This project specific config will be stored into the config.ini, into
 .3mf and .amf and .gcode, and recover it from the same files.

---
 xs/src/slic3r/GUI/PresetBundle.cpp | 9 +++++++++
 xs/src/slic3r/GUI/PresetBundle.hpp | 5 +++++
 xs/xsp/GUI_Preset.xsp              | 2 ++
 3 files changed, 16 insertions(+)

diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index bf79c6562..7d2501a65 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -32,6 +32,9 @@
 
 namespace Slic3r {
 
+static std::vector<std::string> s_project_options {
+};
+
 PresetBundle::PresetBundle() :
     prints(Preset::TYPE_PRINT, Preset::print_options()), 
     filaments(Preset::TYPE_FILAMENT, Preset::filament_options()), 
@@ -56,6 +59,8 @@ PresetBundle::PresetBundle() :
     this->filaments.load_bitmap_default("spool.png");
     this->printers .load_bitmap_default("printer_empty.png");
     this->load_compatible_bitmaps();
+
+    this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options);
 }
 
 PresetBundle::~PresetBundle()
@@ -223,6 +228,7 @@ DynamicPrintConfig PresetBundle::full_config() const
     out.apply(FullPrintConfig());
     out.apply(this->prints.get_edited_preset().config);
     out.apply(this->printers.get_edited_preset().config);
+    out.apply(this->project_config);
 
     auto   *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter"));
     size_t  num_extruders   = nozzle_diameter->values.size();
@@ -412,6 +418,9 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
         }
     }
 
+    // 4) Load the project config values (the per extruder wipe matrix etc).
+    this->project_config.apply_only(config, s_project_options);
+
     this->update_compatible_with_printer(false);
 }
 
diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp
index e6aea206d..6840df75e 100644
--- a/xs/src/slic3r/GUI/PresetBundle.hpp
+++ b/xs/src/slic3r/GUI/PresetBundle.hpp
@@ -39,6 +39,11 @@ public:
     // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
     std::vector<std::string>    filament_presets;
 
+    // The project configuration values are kept separated from the print/filament/printer preset,
+    // they are being serialized / deserialized from / to the .amf, .3mf, .config, .gcode, 
+    // and they are being used by slicing core.
+    DynamicPrintConfig          project_config;
+
     bool                        has_defauls_only() const 
         { return prints.size() <= 1 && filaments.size() <= 1 && printers.size() <= 1; }
 
diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp
index 0033ebd0e..dc8ebf41c 100644
--- a/xs/xsp/GUI_Preset.xsp
+++ b/xs/xsp/GUI_Preset.xsp
@@ -161,6 +161,8 @@ PresetCollection::arrayref()
     Ref<PresetCollection>       print()    %code%{ RETVAL = &THIS->prints;   %};
     Ref<PresetCollection>       filament() %code%{ RETVAL = &THIS->filaments; %};
     Ref<PresetCollection>       printer()  %code%{ RETVAL = &THIS->printers;  %};
+    Ref<DynamicPrintConfig>     project_config() %code%{ RETVAL = &THIS->project_config; %};
+
     bool                        has_defauls_only();
 
     std::vector<std::string>    filament_presets() %code%{ RETVAL = THIS->filament_presets; %};

From 9f18b639a84e43f4f0f783b6023a4fd896269973 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 14 Mar 2018 15:48:37 +0100
Subject: [PATCH 037/118] Added filament_ramming_parameters and
 filament_ramming_buttons into configuration layer

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  2 +-
 xs/src/libslic3r/PrintConfig.cpp            | 12 ++++++++++++
 xs/src/libslic3r/PrintConfig.hpp            |  4 ++++
 xs/src/slic3r/GUI/Preset.cpp                |  7 ++++---
 4 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 3c2f82ffd..160848a47 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -56,7 +56,7 @@ std::istream& operator>>(std::istream& stream, std::vector<T>& vect) {
         we_read_something = true;
     }
     if (!stream.eof() && we_read_something) { // if this is not eof, we might be at separator - let's get rid of it
-        stream.clear();     // if we failed on very first line or reached eof, return stream in !good() state
+        stream.clear();     // if we failed on very first line or reached eof, return stream in good() state
         stream.get();       // get() whatever we are stuck at
     }
     return stream;
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index b0b1c4e99..198d78ad3 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -479,6 +479,18 @@ PrintConfigDef::PrintConfigDef()
     def->min = 0;
     def->default_value = new ConfigOptionInts { 14 };
 
+    def = this->add("filament_ramming_parameters", coStrings);
+    def->label = L("Ramming parameters");
+    def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
+    def->cli = "filament-ramming-parameters=s@";
+    def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0" };
+
+    def = this->add("filament_ramming_buttons", coStrings);
+    def->label = L("Draggable button in RammingDialog");
+    def->tooltip = L("This string is edited by RammingDialog and contains position of draggable buttons ");
+    def->cli = "filament-ramming-buttons=s@";
+    def->default_value = new ConfigOptionStrings { "0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" };
+
     def = this->add("filament_diameter", coFloats);
     def->label = L("Diameter");
     def->tooltip = L("Enter your filament diameter here. Good precision is required, so use a caliper "
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 501a8ac36..c6d7ce277 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -471,6 +471,8 @@ public:
     ConfigOptionFloats              filament_unloading_speed;
     ConfigOptionFloats              filament_toolchange_delay;
     ConfigOptionInts                filament_cooling_time;
+    ConfigOptionStrings             filament_ramming_parameters;
+    ConfigOptionStrings             filament_ramming_buttons;
     ConfigOptionBool                gcode_comments;
     ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
     ConfigOptionString              layer_gcode;
@@ -527,6 +529,8 @@ protected:
         OPT_PTR(filament_unloading_speed);
         OPT_PTR(filament_toolchange_delay);
         OPT_PTR(filament_cooling_time);
+        OPT_PTR(filament_ramming_parameters);
+        OPT_PTR(filament_ramming_buttons);
         OPT_PTR(gcode_comments);
         OPT_PTR(gcode_flavor);
         OPT_PTR(layer_gcode);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 92bfef416..c3a988e65 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -211,9 +211,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", "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"
+        "filament_cooling_time", "filament_ramming_buttons", "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"
     };
     return s_opts;
 }

From 67009d80fd83d5bb3951e9e4b4cc73824e0cad6f Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 15 Mar 2018 14:04:12 +0100
Subject: [PATCH 038/118] Ramming parameters reduced to one and connected to
 the wipe tower generator again

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp |  19 +-
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 106 +++--------
 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/RammingChart.hpp          |   4 +-
 xs/src/slic3r/GUI/Tab.cpp                   |   8 +-
 xs/src/slic3r/GUI/WipeTowerDialog.cpp       | 187 +++++++++-----------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp       |  15 +-
 10 files changed, 139 insertions(+), 217 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index b94950af1..ab5429615 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -465,6 +465,7 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
 	return INVALID;
 }
 
+
 // Returns gcode to prime the nozzles at the front edge of the print bed.
 WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	// print_z of the first layer.
@@ -478,7 +479,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
 	this->m_num_layer_changes 	= 0;
 	this->m_current_tool 		= tools.front();
-
+    
     // The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
     // Due to the XYZ calibration, this working space may shrink slightly from all directions,
     // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
@@ -723,8 +724,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	
 	writer.append("; CP TOOLCHANGE UNLOAD\n");
 	
-	const float line_width = m_line_width * m_par.ramming_line_width_multiplicator[m_current_tool];       // desired ramming line thickness
-	const float y_step = line_width * m_par.ramming_step_multiplicator[m_current_tool] * m_extra_spacing; // spacing between lines in mm
+	const float line_width = m_line_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
 
 	unsigned i = 0;										// iterates through ramming_speed
 	m_left_to_right = true;								// current direction of ramming
@@ -778,10 +779,10 @@ void WipeTowerPrusaMM::toolchange_Unload(
     }
 
     // now the ramming itself:
-    while (i < m_par.ramming_speed[m_current_tool].size())
+    while (i < m_filpar[m_current_tool].ramming_speed.size())
     {
-        const float x = volume_to_length(m_par.ramming_speed[m_current_tool][i] * 0.25f, line_width, m_layer_height);
-        const float e = m_par.ramming_speed[m_current_tool][i] * 0.25f / Filament_Area; // transform volume per sec to E move;
+        const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height);
+        const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move;
         const float dist = std::min(x - e_done, remaining);		  // distance to travel for either the next 0.25s, or to the next turnaround
         const float actual_time = dist/x * 0.25;
         writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
@@ -1092,10 +1093,10 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 	// this is an actual toolchange - let's calculate depth to reserve on the wipe tower
 	float depth = 0.f;			
 	float width = m_wipe_tower_width - 3*m_perimeter_width; 
-	float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_par.ramming_speed[old_tool].begin(), m_par.ramming_speed[old_tool].end(), 0.f),
-										m_line_width * m_par.ramming_line_width_multiplicator[old_tool],
+	float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
+										m_line_width * m_filpar[old_tool].ramming_line_width_multiplicator,
 										layer_height_par);
-	depth = (int(length_to_extrude / width) + 1) * (m_line_width * m_par.ramming_line_width_multiplicator[old_tool] * m_par.ramming_step_multiplicator[old_tool]);
+	depth = (int(length_to_extrude / width) + 1) * (m_line_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator);
     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;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 160848a47..4ecdb5f42 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -67,82 +67,11 @@ std::istream& operator>>(std::istream& stream, std::vector<T>& vect) {
 struct WipeTowerParameters {
     WipeTowerParameters() {  }           // create new empty object
     WipeTowerParameters(const std::string& init_data) { // create object and initialize from std::string
-        std::istringstream in(init_data);               // validation of input is left to the caller
-        in >> sampling;
-        for (std::vector<float> vect{} ; in >> vect ;) {  // until we get to fail state ("**")...
-            if (vect.size()>=2) {
-                ramming_line_width_multiplicator.push_back(vect[0]);
-                ramming_step_multiplicator.push_back(vect[1]);
-                vect.erase(vect.begin(),vect.begin()+2);
-            }
-            else vect.clear(); // something's not right, we will restore defaults anyway
-            ramming_speed.push_back(vect);
-
-            if (in.good()) {
-                in >> vect;
-                std::vector<std::pair<float,float>> pairs;
-                for (unsigned int i=0;i<vect.size();++i)
-                    if (i%2==1)
-                        pairs.push_back(std::make_pair(vect[i-1],vect[i]));
-                ramming_buttons.push_back(pairs);
-            }
-        }
-        in.clear();
-        in.get();
-
-        for (std::vector<float> vect{} ; in >> vect ;) {  // let's keep reading
-            wipe_volumes.push_back(vect);
-        }
-        in.clear();
-        in.get();
-
-        std::vector<int> vect{};
-        in >> vect;
-        for (unsigned int i=0;i<vect.size();++i)
-            if (i%2==1)
-                filament_wipe_volumes.push_back(std::make_pair(vect[i-1],vect[i]));
-        
-        if (!validate())    // in case we did not parse the input right
-            set_defaults();
-    }
-
-    std::string to_string() {
-        std::ostringstream out;
-        out << sampling << "\n";
-        for (unsigned extruder=0;extruder<ramming_step_multiplicator.size();++extruder) {
-            out << "\n" << ramming_line_width_multiplicator[extruder] << " " 
-                << ramming_step_multiplicator[extruder] << " " << ramming_speed[extruder]  << "*"
-                << ramming_buttons[extruder] << "*";
-        }
-        out << "*\n";
-        for (auto& radek : wipe_volumes)
-            out << "\n" << radek << "*";
-        out << "*\n";
-        out << filament_wipe_volumes << "*";
-        return out.str();
-    }
-
-    bool validate() const {     // basic check for validity to distinguish most dramatic failures
-        const unsigned int num = ramming_step_multiplicator.size();
-        if ( num < 1 || ramming_line_width_multiplicator.size()!=num || ramming_step_multiplicator.size()!=num ||
-             ramming_buttons.size()!=num || wipe_volumes.size()!=num ||
-             filament_wipe_volumes.size()!=num)
-            return false;
-        for (const auto& row : wipe_volumes)
-            if (row.size()!=num)
-                return false;
-        return true;
+        set_defaults();
     }
+    
     void set_defaults() {
         sampling = 0.25f;
-        ramming_line_width_multiplicator = {1.5f, 1.5f, 1.5f, 1.5f};
-        ramming_step_multiplicator = {1.1f, 1.1f, 1.1f, 1.1f};
-        ramming_speed.clear();
-        ramming_buttons.clear();
-        for (unsigned int i=0;i<4;++i) {
-            ramming_speed.push_back(std::vector<float>{7.6f, 7.6f, 7.6f, 7.6f, 9.f, 9.f, 9.f, 10.7f, 10.7f, 10.7f});
-            ramming_buttons.push_back(std::vector<std::pair<float,float>>{{0.05f, 6.6f},{0.45f, 6.8f},{0.95f, 7.8f},{1.45f, 8.3f},{1.95f, 9.7f},{2.45f,10.f},{2.95f, 7.6f},{3.45f, 7.6f},{3.95f, 7.6f},{4.45f, 7.6f},{4.95f, 7.6f}});
-        }
         wipe_volumes = {{  0.f, 60.f, 60.f, 60.f},
                         { 60.f,  0.f, 60.f, 60.f},
                         { 60.f, 60.f,  0.f, 60.f},
@@ -151,10 +80,6 @@ struct WipeTowerParameters {
     }
     
     float sampling = 0.25f; // this does not quite work yet, keep it fixed to 0.25f
-    std::vector<float> ramming_line_width_multiplicator;
-    std::vector<float> ramming_step_multiplicator;
-    std::vector<std::vector<float>> ramming_speed;
-    std::vector<std::vector<std::pair<float,float>>> ramming_buttons;
     std::vector<std::vector<float>> wipe_volumes;
     std::vector<std::pair<int,int>> filament_wipe_volumes;
 };
@@ -222,7 +147,7 @@ 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, int cooling_time)
+                      float unloading_speed, float delay, int cooling_time, std::string ramming_parameters)
 	{
         m_filpar[idx].material = material;
         m_filpar[idx].temperature = temp;
@@ -231,6 +156,14 @@ public:
         m_filpar[idx].unloading_speed = unloading_speed;
         m_filpar[idx].delay = delay;
         m_filpar[idx].cooling_time = cooling_time;
+        
+        std::stringstream stream{ramming_parameters};
+        float speed = 0.f;
+        stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
+        m_filpar[idx].ramming_line_width_multiplicator /= 100;
+        m_filpar[idx].ramming_step_multiplicator /= 100;
+        while (stream >> speed)
+            m_filpar[idx].ramming_speed.push_back(speed);
 	}
 
 
@@ -345,13 +278,16 @@ private:
 
 
     struct FilamentParameters {
-        material_type 	material;
-        int  			temperature;
-        int  			first_layer_temperature;
-        float           loading_speed;
-        float           unloading_speed;
-        float           delay;
-        int             cooling_time;
+        material_type 	    material;
+        int  			    temperature;
+        int  			    first_layer_temperature;
+        float               loading_speed;
+        float               unloading_speed;
+        float               delay;
+        int                 cooling_time;
+        float               ramming_line_width_multiplicator;
+        float               ramming_step_multiplicator;
+        std::vector<float>  ramming_speed;
     };
 
 	// Extruder specific parameters.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 95c57339a..ae34b47d8 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -189,6 +189,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "filament_unloading_speed"
             || opt_key == "filament_toolchange_delay"
             || opt_key == "filament_cooling_time"
+            || opt_key == "filament_ramming_parameters"
             || opt_key == "gcode_flavor"
             || opt_key == "single_extruder_multi_material"
             || opt_key == "spiral_vase"
@@ -1049,7 +1050,8 @@ 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_toolchange_delay.get_at(i));
+            this->config.filament_cooling_time.get_at(i),
+            this->config.filament_ramming_parameters.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.
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 198d78ad3..081c05b2d 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -483,13 +483,8 @@ PrintConfigDef::PrintConfigDef()
     def->label = L("Ramming parameters");
     def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
     def->cli = "filament-ramming-parameters=s@";
-    def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0" };
-
-    def = this->add("filament_ramming_buttons", coStrings);
-    def->label = L("Draggable button in RammingDialog");
-    def->tooltip = L("This string is edited by RammingDialog and contains position of draggable buttons ");
-    def->cli = "filament-ramming-buttons=s@";
-    def->default_value = new ConfigOptionStrings { "0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" };
+    def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|"
+	   " 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" };
 
     def = this->add("filament_diameter", coFloats);
     def->label = L("Diameter");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index c6d7ce277..ecec43ae1 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -472,7 +472,6 @@ public:
     ConfigOptionFloats              filament_toolchange_delay;
     ConfigOptionInts                filament_cooling_time;
     ConfigOptionStrings             filament_ramming_parameters;
-    ConfigOptionStrings             filament_ramming_buttons;
     ConfigOptionBool                gcode_comments;
     ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
     ConfigOptionString              layer_gcode;
@@ -530,7 +529,6 @@ protected:
         OPT_PTR(filament_toolchange_delay);
         OPT_PTR(filament_cooling_time);
         OPT_PTR(filament_ramming_parameters);
-        OPT_PTR(filament_ramming_buttons);
         OPT_PTR(gcode_comments);
         OPT_PTR(gcode_flavor);
         OPT_PTR(layer_gcode);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index c3a988e65..5049c9cf6 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -211,7 +211,7 @@ 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_buttons", "filament_ramming_parameters", "temperature", "first_layer_temperature", "bed_temperature",
+        "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"
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
index 520b5808e..5ec8b33ee 100644
--- a/xs/src/slic3r/GUI/RammingChart.hpp
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -13,11 +13,11 @@ wxDECLARE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
 class Chart : public wxWindow {
         
 public:
-    Chart(wxWindow* parent, wxRect rect,const std::vector<std::pair<float,float>>& initial_buttons,std::vector<float> ramming_speed, float sampling) :
+    Chart(wxWindow* parent, wxRect rect,const std::vector<std::pair<float,float>>& initial_buttons,int ramming_speed_size, float sampling) :
         wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize())
     {
         m_rect=wxRect(wxPoint(30,0),rect.GetSize()-wxSize(30,30));
-        visible_area = wxRect2DDouble(0.0, 0.0, sampling*ramming_speed.size(), 20.);
+        visible_area = wxRect2DDouble(0.0, 0.0, sampling*ramming_speed_size, 20.);
         m_buttons.clear();
         if (initial_buttons.size()>0)
             for (const auto& pair : initial_buttons)
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 58d714fc7..ef95da90b 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -469,7 +469,7 @@ void TabPrint::build()
 			{
                 std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
                 std::cout << "dialog init: " << init_data << std::endl;
-				WipeTowerDialog dlg(this,init_data); // dlg lives on stack, no need to call Destroy
+				WipingDialog dlg(this,init_data); // dlg lives on stack, no need to call Destroy
 
 				if (dlg.ShowModal() == wxID_OK) {
                     load_key_value("wipe_tower_advanced", dlg.GetValue());
@@ -891,10 +891,10 @@ void TabFilament::build()
             
             ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
 			{
-                //std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
-                RammingDialog dlg(this,std::string());
+                RammingDialog dlg(this,(m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0));
                 if (dlg.ShowModal() == wxID_OK) {
-                    //load_key_value("wipe_tower_advanced", dlg.GetValue());
+                    wxMessageBox("DIALOG OK:\n"+dlg.get_parameters());
+                    //load_key_value("wipe_tower_advanced", dlg.get_parameters());
                 }
 			}));
 			return sizer;
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 8bf51e4e5..8e2746329 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -1,52 +1,13 @@
 #include <algorithm>
+#include <sstream>
 #include "WipeTowerDialog.hpp"
 
-// Human-readable output of Parameters structure
-std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par) {
-    str << "sampling: " << par.sampling << "\n"; 
-   
-    str << "line widths: ";
-    for (const auto& a : par.ramming_line_width_multiplicator) str << a << " ";
-    
-    str << "line spacing: ";
-    for (const auto& a : par.ramming_step_multiplicator) str << a << " ";
-    
-    str<<"\n\nramming_speeds:\n";
-    for (const auto& a : par.ramming_speed) {
-        for (const auto& b : a)
-            str << b << " ";
-        str<<"\n";
-    }
-    str<<"\n\nramming_buttons:\n";
-    for (const auto& a : par.ramming_buttons) {
-        for (const auto& b : a) {
-            Slic3r::operator <<(str,b); // temporary hack (this << is in the namespace Slic3r)
-            str << " | ";               // the function will be deleted after everything is debugged, anyway
-        }
-        str<<"\n";
-    }
-    str<<"\n\nwipe volumes:\n";
-    for (const auto& a : par.wipe_volumes) {
-        for (const auto& b : a)
-            str << b << " ";
-        str<<"\n";
-    }
-    str<<"\n\nfilament wipe volumes:\n";
-    for (const auto& a : par.filament_wipe_volumes) {
-        Slic3r::operator <<(str,a);
-        str << " ";
-    }
-    str<<"\n";
-        
-    return str;
-}
 
-
-RammingDialog::RammingDialog(wxWindow* parent,const std::string& init_data)
+RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
 : wxDialog(parent, -1,  wxT("Ramming customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
     this->Centre();
-    m_panel_ramming  = new RammingPanel(this,Slic3r::WipeTowerParameters(std::string()));
+    m_panel_ramming  = new RammingPanel(this,parameters);
     m_panel_ramming->Show(true);
     this->Show();
 
@@ -60,7 +21,7 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& init_data)
     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
     
     this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
-       // m_output_data=read_dialog_values();
+        m_output_data = m_panel_ramming->get_parameters();
         EndModal(wxID_OK);
         },wxID_OK);
 }
@@ -69,7 +30,7 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& init_data)
 
 
 
-RammingPanel::RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
 : wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
 {
     new wxStaticText(this,wxID_ANY,wxString("Total ramming time (s):"),     wxPoint(500,105),      wxSize(200,25),wxALIGN_LEFT);
@@ -81,8 +42,22 @@ RammingPanel::RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p
     new wxStaticText(this,wxID_ANY,wxString("Ramming line spacing (%):"),   wxPoint(500,235),      wxSize(200,25),wxALIGN_LEFT);
     m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,     wxPoint(700,230),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        
     
+    std::stringstream stream{parameters};
+    stream >> m_ramming_line_width_multiplicator >> m_ramming_step_multiplicator;
+    int ramming_speed_size = 0;
+    float dummy = 0.f;
+    while (stream >> dummy)
+        ++ramming_speed_size;
+    stream.clear();
+    stream.get();    
     
-    m_chart = new Chart(this,wxRect(10,10,480,360),p.ramming_buttons[0],p.ramming_speed[0],p.sampling);
+    std::vector<std::pair<float,float>> buttons;
+    float x = 0.f;
+    float y = 0.f;
+    while (stream >> x >> y)
+        buttons.push_back(std::make_pair(x,y));        
+    
+    m_chart = new Chart(this,wxRect(10,10,480,360),buttons,ramming_speed_size,0.25f);
     
     m_widget_time->SetValue(m_chart->get_time());
     m_widget_time->SetDigits(2);
@@ -100,26 +75,83 @@ RammingPanel::RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p
     Refresh(this);
 }
 
-
-void RammingPanel::fill_parameters(Slic3r::WipeTowerParameters& p)
-{
-    if (!m_chart) return;
-    p.ramming_buttons[0] = m_chart->get_buttons();
-    p.ramming_speed[0]   = m_chart->get_ramming_speed(p.sampling);
-    p.ramming_line_width_multiplicator.push_back(m_ramming_line_width_multiplicator/100.f);
-    p.ramming_step_multiplicator.push_back(m_ramming_step_multiplicator/100.f);
-}
-
-
 void RammingPanel::line_parameters_changed() {
     m_ramming_line_width_multiplicator = m_widget_ramming_line_width_multiplicator->GetValue();
     m_ramming_step_multiplicator = m_widget_ramming_step_multiplicator->GetValue();
 }
 
+std::string RammingPanel::get_parameters()
+{
+    std::vector<float> speeds = m_chart->get_ramming_speed(0.25f);
+    std::vector<std::pair<float,float>> buttons = m_chart->get_buttons();
+    std::stringstream stream;
+    stream << m_ramming_line_width_multiplicator << " " << m_ramming_step_multiplicator;
+    for (const float& speed_value : speeds)
+        stream << " " << speed_value;
+    stream << "|";    
+    for (const auto& button : buttons)
+        stream << " " << button.first << " " << button.second;
+    return stream.str();
+}
 
 
 
 
+
+
+
+
+/*
+void WipingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
+    p.wipe_volumes.clear();
+    p.filament_wipe_volumes.clear();
+    for (int i=0;i<4;++i) {
+        // first go through the full matrix:
+        p.wipe_volumes.push_back(std::vector<float>());
+        for (int j=0;j<4;++j) {
+            double val = 0.;
+            edit_boxes[j][i]->GetValue().ToDouble(&val);
+            p.wipe_volumes[i].push_back((float)val);  
+        }
+        
+        // now the filament volumes:
+        p.filament_wipe_volumes.push_back(std::make_pair(m_old[i]->GetValue(),m_new[i]->GetValue()));
+    }
+}
+*/
+
+
+
+WipingDialog::WipingDialog(wxWindow* parent,const std::string& init_data)
+: wxDialog(parent, -1,  wxT("Wiping customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+{
+    this->Centre();
+            
+    Slic3r::WipeTowerParameters parameters(init_data);
+    /*if (!parameters.validate()) {
+        wxMessageDialog(this,"Wipe tower parameters not parsed correctly!\nRestoring default settings.","Error",wxICON_ERROR);
+        parameters.set_defaults();
+    }*/
+    m_panel_wiping  = new WipingPanel(this,parameters);
+    this->Show();
+
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(m_panel_wiping, 1, wxEXPAND);
+    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
+    SetSizer(main_sizer);
+    SetMinSize(GetSize());
+    main_sizer->SetSizeHints(this);
+    
+    this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
+    
+    this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
+        m_output_data=read_dialog_values();
+        EndModal(wxID_OK);
+        },wxID_OK);
+}
+
+
+
 WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
 : wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
 {
@@ -153,23 +185,6 @@ WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
     Refresh(this);
 }
 
-void WipingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
-    p.wipe_volumes.clear();
-    p.filament_wipe_volumes.clear();
-    for (int i=0;i<4;++i) {
-        // first go through the full matrix:
-        p.wipe_volumes.push_back(std::vector<float>());
-        for (int j=0;j<4;++j) {
-            double val = 0.;
-            edit_boxes[j][i]->GetValue().ToDouble(&val);
-            p.wipe_volumes[i].push_back((float)val);  
-        }
-        
-        // now the filament volumes:
-        p.filament_wipe_volumes.push_back(std::make_pair(m_old[i]->GetValue(),m_new[i]->GetValue()));
-    }
-}
-
 void WipingPanel::fill_in_matrix() {
     wxArrayString choices;
     choices.Add("sum");
@@ -192,31 +207,5 @@ void WipingPanel::fill_in_matrix() {
 
 
 
-WipeTowerDialog::WipeTowerDialog(wxWindow* parent,const std::string& init_data)
-: wxDialog(parent, -1,  wxT("Wiping customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
-{
-    this->Centre();
-            
-    Slic3r::WipeTowerParameters parameters(init_data);
-    if (!parameters.validate()) {
-        wxMessageDialog(this,"Wipe tower parameters not parsed correctly!\nRestoring default settings.","Error",wxICON_ERROR);
-        parameters.set_defaults();
-    }
-    m_panel_wiping  = new WipingPanel(this,parameters);
-    this->Show();
 
-    auto main_sizer = new wxBoxSizer(wxVERTICAL);
-    main_sizer->Add(m_panel_wiping, 1, wxEXPAND);
-    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
-    SetSizer(main_sizer);
-    SetMinSize(GetSize());
-    main_sizer->SetSizeHints(this);
-    
-    this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
-    
-    this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
-        m_output_data=read_dialog_values();
-        EndModal(wxID_OK);
-        },wxID_OK);
-}
 
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index f9a9f9524..92624037b 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -13,14 +13,13 @@
 #include "RammingChart.hpp"
 
 
-// Human-readable output of Parameters structure
 std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par);
 
 class RammingPanel : public wxPanel {
 public:
     RammingPanel(wxWindow* parent);
-    RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
-    void fill_parameters(Slic3r::WipeTowerParameters& p);
+    RammingPanel(wxWindow* parent,const std::string& data);
+    std::string get_parameters();
 
 private:
     Chart* m_chart = nullptr;
@@ -37,9 +36,11 @@ private:
 
 class RammingDialog : public wxDialog {
 public:
-    RammingDialog(wxWindow* parent,const std::string& init_data);    
+    RammingDialog(wxWindow* parent,const std::string& parameters);    
+    std::string get_parameters() { return m_output_data; }
 private:
     RammingPanel* m_panel_ramming = nullptr;
+    std::string m_output_data;
 };
 
 
@@ -63,9 +64,9 @@ private:
 
 
 
-class WipeTowerDialog : public wxDialog {
+class WipingDialog : public wxDialog {
 public:
-    WipeTowerDialog(wxWindow* parent,const std::string& init_data);
+    WipingDialog(wxWindow* parent,const std::string& init_data);
     
     std::string GetValue() const { return m_output_data; }
     
@@ -78,7 +79,7 @@ private:
     std::string read_dialog_values() {
         Slic3r::WipeTowerParameters p;
         m_panel_wiping ->fill_parameters(p);
-        return p.to_string();
+        //return p.to_string();
     }
 };
 

From b556cec42d25291fa167da745c9b985490b0a8ac Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 16 Mar 2018 13:58:58 +0100
Subject: [PATCH 039/118] Got rid of wipe_tower_advanced option

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  2 +-
 xs/src/libslic3r/Print.cpp                  |  3 +--
 xs/src/libslic3r/PrintConfig.cpp            | 24 +++++++++++++++------
 xs/src/libslic3r/PrintConfig.hpp            |  6 +++---
 xs/src/slic3r/GUI/Preset.cpp                |  2 +-
 xs/src/slic3r/GUI/Tab.cpp                   |  9 ++++----
 xs/src/slic3r/GUI/WipeTowerDialog.cpp       | 21 +++++++-----------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp       | 10 ++++-----
 8 files changed, 40 insertions(+), 37 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 4ecdb5f42..d5dc4f962 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -110,7 +110,7 @@ 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 wipe_area, float rotation_angle, float cooling_tube_retraction,
-                     float cooling_tube_length, float parking_pos_retraction, float bridging, bool adhesion, std::string& parameters,
+                     float cooling_tube_length, float parking_pos_retraction, float bridging, bool adhesion, const std::string& parameters,
                      unsigned int initial_tool) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index ae34b47d8..6a804c10d 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -195,7 +195,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "spiral_vase"
             || opt_key == "temperature"
             || opt_key == "wipe_tower"
-            || opt_key == "wipe_tower_advanced"
             || opt_key == "wipe_tower_x"
             || opt_key == "wipe_tower_y"
             || opt_key == "wipe_tower_width"
@@ -1035,7 +1034,7 @@ void Print::_make_wipe_tower()
         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), bool(this->config.wipe_tower_adhesion),
-        this->config.wipe_tower_advanced.value,m_tool_ordering.first_extruder());
+        /*this->config.wipe_tower_advanced.value*/std::string(""),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 081c05b2d..6d40b8582 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1773,12 +1773,24 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "wipe-tower!";
     def->default_value = new ConfigOptionBool(false);
 
-    def = this->add("wipe_tower_advanced", coString);
-    def->label = L("Advanced string");
-    def->tooltip = L("Advanced tooltip ");
-    def->sidetext = L("advanced sidetext");
-    def->cli = "wipe-tower-advanced=s";
-    def->default_value = new ConfigOptionString("");
+    def = this->add("wiping_volumes_extruders", coFloats);
+    def->label = L("Extrusion multiplier");
+    def->tooltip = L("This vector saves required volumes to change from/to each tool used on the "
+                     "wipe tower. These values are used to simplify creation of the full purging "
+                     "volumes below. ");
+    def->cli = "wiping-volumes-extruders=f@";
+    def->default_value = new ConfigOptionFloats { 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f  };
+
+    def = this->add("wiping_volumes_matrix", coFloats);
+    def->label = L("Extrusion multiplier");
+    def->tooltip = L("This matrix describes volumes (in cubic milimetres) required to purge the"
+                     " new filament on the wipe tower for any given pair of tools. ");
+    def->cli = "wiping-volumes-matrix=f@";
+    def->default_value = new ConfigOptionFloats {   0.f, 100.f, 100.f, 100.f, 100.f,
+                                                  100.f,   0.f, 100.f, 100.f, 100.f,
+                                                  100.f, 100.f,   0.f, 100.f, 100.f,
+                                                  100.f, 100.f, 100.f,   0.f, 100.f,
+                                                  100.f, 100.f, 100.f, 100.f,   0.f };
 
     def = this->add("wipe_tower_x", coFloat);
     def->label = L("Position X");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index ecec43ae1..af0c814e1 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -500,7 +500,8 @@ public:
     ConfigOptionFloat               cooling_tube_retraction;
     ConfigOptionFloat               cooling_tube_length;
     ConfigOptionFloat               parking_pos_retraction;
-    
+
+
     std::string get_extrusion_axis() const
     {
         return
@@ -622,7 +623,6 @@ public:
     ConfigOptionInt                 threads;
     ConfigOptionBools               wipe;
     ConfigOptionBool                wipe_tower;
-    ConfigOptionString              wipe_tower_advanced;
     ConfigOptionFloat               wipe_tower_x;
     ConfigOptionFloat               wipe_tower_y;
     ConfigOptionFloat               wipe_tower_width;
@@ -690,7 +690,6 @@ protected:
         OPT_PTR(threads);
         OPT_PTR(wipe);
         OPT_PTR(wipe_tower);
-        OPT_PTR(wipe_tower_advanced);
         OPT_PTR(wipe_tower_x);
         OPT_PTR(wipe_tower_y);
         OPT_PTR(wipe_tower_width);
@@ -736,6 +735,7 @@ class FullPrintConfig :
 public:
     // Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
     std::string                 validate();
+
 protected:
     // Protected constructor to be called to initialize ConfigCache::m_default.
     FullPrintConfig(int) : PrintObjectConfig(0), PrintRegionConfig(0), PrintConfig(0), HostConfig(0) {}
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 5049c9cf6..e2706c5db 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -198,7 +198,7 @@ const std::vector<std::string>& Preset::print_options()
         "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", 
         "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_advanced", "wipe_tower", "wipe_tower_x",
+        "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x",
         "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_adhesion", "wipe_tower_bridging",
         "compatible_printers", "compatible_printers_condition"
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index ef95da90b..473e0b13c 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -467,13 +467,12 @@ void TabPrint::build()
 			sizer->Add(m_wipe_tower_btn);
 			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
 			{
-                std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
-                std::cout << "dialog init: " << init_data << std::endl;
-				WipingDialog dlg(this,init_data); // dlg lives on stack, no need to call Destroy
+                //auto init_data = (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
+				//WipingDialog dlg(this,std::vector<float>(init_data.begin(), init_data.end())); // dlg lives on stack, no need to call Destroy
 
 				if (dlg.ShowModal() == wxID_OK) {
-                    load_key_value("wipe_tower_advanced", dlg.GetValue());
-                    std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
+                    //load_key_value("wipe_tower_advanced", dlg.GetValue());
+                    //std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
                 }
 			}));
 			return sizer;
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 8e2746329..1c69285b7 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -122,17 +122,12 @@ void WipingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
 
 
 
-WipingDialog::WipingDialog(wxWindow* parent,const std::string& init_data)
+WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& init_data)
 : wxDialog(parent, -1,  wxT("Wiping customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
     this->Centre();
-            
-    Slic3r::WipeTowerParameters parameters(init_data);
-    /*if (!parameters.validate()) {
-        wxMessageDialog(this,"Wipe tower parameters not parsed correctly!\nRestoring default settings.","Error",wxICON_ERROR);
-        parameters.set_defaults();
-    }*/
-    m_panel_wiping  = new WipingPanel(this,parameters);
+    
+    m_panel_wiping  = new WipingPanel(this,init_data);
     this->Show();
 
     auto main_sizer = new wxBoxSizer(wxVERTICAL);
@@ -145,14 +140,14 @@ WipingDialog::WipingDialog(wxWindow* parent,const std::string& init_data)
     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
     
     this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
-        m_output_data=read_dialog_values();
+        //m_output_data=read_dialog_values();
         EndModal(wxID_OK);
         },wxID_OK);
 }
 
 
 
-WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+WipingPanel::WipingPanel(wxWindow* parent,const std::vector<float>& data)
 : wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
 {
     const int N = 4; // number of extruders
@@ -162,8 +157,8 @@ WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
     m_widget_button = new wxButton(this,wxID_ANY,"-> Fill in the matrix ->",wxPoint(300,130),wxSize(175,50));
     for (int i=0;i<N;++i) {
         new wxStaticText(this,wxID_ANY,wxString("Filament #")<<i+1<<": ",wxPoint(20,105+30*i) ,wxSize(150,25),wxALIGN_LEFT);
-        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,p.filament_wipe_volumes[i].first));
-        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,p.filament_wipe_volumes[i].second));
+        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,data[2*i]));
+        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,data[2*i+1]));
     }
     
     wxPoint origin(515,55);
@@ -176,7 +171,7 @@ WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
             if (i==j)
                 edit_boxes[i][j]->Disable();
             else
-                edit_boxes[i][j]->SetValue(wxString("")<<int(p.wipe_volumes[j][i]));
+                edit_boxes[i][j]->SetValue(wxString("")<<int(0));//p.wipe_volumes[j][i]));
         }
         new wxStaticText(this,wxID_ANY,wxString("Filament changed to"),origin+wxPoint(75,0) ,wxSize(500,25));            
     }
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index 92624037b..8e84f3dc1 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -48,7 +48,7 @@ private:
 
 class WipingPanel : public wxPanel {
 public:
-    WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+    WipingPanel(wxWindow* parent,const std::vector<float>& data);
     void fill_parameters(Slic3r::WipeTowerParameters& p);
         
 private:
@@ -66,15 +66,13 @@ private:
 
 class WipingDialog : public wxDialog {
 public:
-    WipingDialog(wxWindow* parent,const std::string& init_data);
-    
-    std::string GetValue() const { return m_output_data; }
+    WipingDialog(wxWindow* parent,const std::vector<float>& init_data);
+    std::vector<float> get_value() const { return m_output_data; }
     
     
 private:
-    std::string m_file_name="config_wipe_tower";
     WipingPanel*  m_panel_wiping  = nullptr;
-    std::string m_output_data = "";
+    std::vector<float> m_output_data;
             
     std::string read_dialog_values() {
         Slic3r::WipeTowerParameters p;

From e41eff91868063326fc4e163c33a6201d0fe8149 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 19 Mar 2018 15:30:54 +0100
Subject: [PATCH 040/118] Wiping volumes options temporarily added to Print
 Settings

---
 xs/src/libslic3r/PrintConfig.hpp   | 4 ++++
 xs/src/slic3r/GUI/Preset.cpp       | 2 +-
 xs/src/slic3r/GUI/PresetBundle.cpp | 2 +-
 xs/src/slic3r/GUI/Tab.cpp          | 7 ++++---
 4 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 3488b5c4f..b35cf9cf1 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -630,6 +630,8 @@ public:
     ConfigOptionFloat               wipe_tower_rotation_angle;
     ConfigOptionFloat               wipe_tower_bridging;
     ConfigOptionBool                wipe_tower_adhesion;
+    ConfigOptionFloats              wiping_volumes_matrix;
+    ConfigOptionFloats              wiping_volumes_extruders;
     ConfigOptionFloat               z_offset;
     
 protected:
@@ -698,6 +700,8 @@ protected:
         OPT_PTR(wipe_tower_rotation_angle);
         OPT_PTR(wipe_tower_bridging);
         OPT_PTR(wipe_tower_adhesion);
+        OPT_PTR(wiping_volumes_matrix);
+        OPT_PTR(wiping_volumes_extruders);
         OPT_PTR(z_offset);
     }
 };
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 57fa97774..847c7e523 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -200,7 +200,7 @@ const std::vector<std::string>& Preset::print_options()
         "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_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_adhesion", "wipe_tower_bridging",
-        "compatible_printers", "compatible_printers_condition"
+        "wiping_volumes_matrix", "wiping_volumes_extruders", "compatible_printers", "compatible_printers_condition"
 
     };
     return s_opts;
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 7d2501a65..668b9ee12 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -32,7 +32,7 @@
 
 namespace Slic3r {
 
-static std::vector<std::string> s_project_options {
+static std::vector<std::string> s_project_options {   
 };
 
 PresetBundle::PresetBundle() :
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 8cec658b3..6e978f083 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -528,11 +528,12 @@ void TabPrint::build()
 			sizer->Add(m_wipe_tower_btn);
 			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
 			{
-                //auto init_data = (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
-				//WipingDialog dlg(this,std::vector<float>(init_data.begin(), init_data.end())); // dlg lives on stack, no need to call Destroy
+                std::vector<double> init_data = (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
+                load_key_value("wiping_volumes_matrix", a);
+                WipingDialog dlg(this,std::vector<float>(init_data.begin(), init_data.end())); // dlg lives on stack, no need to call Destroy
 
 				if (dlg.ShowModal() == wxID_OK) {
-                    //load_key_value("wipe_tower_advanced", dlg.GetValue());
+                    //load_key_value("wiping_volumes_matrix", dlg.GetValue());
                     //std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
                 }
 			}));

From 349a8a88aec920c0970fe384f6ee94cab5e8a9a9 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 19 Mar 2018 15:35:31 +0100
Subject: [PATCH 041/118] Bug fix: speed for finish_layer was not properly set

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 2 +-
 xs/src/slic3r/GUI/Tab.cpp                   | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index ab5429615..59fa7dced 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -1014,7 +1014,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
             else box.expand(-m_perimeter_width);
         }
         else i=2;	// only draw the inner perimeter, outer has been already drawn by tool_change(...)
-        writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y);
+        writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y,2900*speed_factor);
     }
 
     // we are in one of the corners, travel to ld along the perimeter:
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 6e978f083..d220130fd 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -529,7 +529,6 @@ void TabPrint::build()
 			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
 			{
                 std::vector<double> init_data = (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
-                load_key_value("wiping_volumes_matrix", a);
                 WipingDialog dlg(this,std::vector<float>(init_data.begin(), init_data.end())); // dlg lives on stack, no need to call Destroy
 
 				if (dlg.ShowModal() == wxID_OK) {

From 76b280c64caab570c5b3e3fb6704cfa9a767797d Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 19 Mar 2018 15:53:49 +0100
Subject: [PATCH 042/118] Removed parameter 'wipe_tower_adhesion', it will
 always be true

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index d5dc4f962..8cfc5872c 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -110,7 +110,7 @@ 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 wipe_area, float rotation_angle, float cooling_tube_retraction,
-                     float cooling_tube_length, float parking_pos_retraction, float bridging, bool adhesion, const std::string& parameters,
+                     float cooling_tube_length, float parking_pos_retraction, float bridging, const std::string& parameters,
                      unsigned int initial_tool) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
@@ -126,7 +126,6 @@ public:
         m_par(parameters) 
  	{
         m_bridging = bridging;
-        m_adhesion = adhesion;
         
 		for (size_t i = 0; i < 4; ++ i) {
 			// Extruder specific parameters.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index a126d84b7..0167b8ba7 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -202,7 +202,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "wipe_tower_per_color_wipe"
             || opt_key == "wipe_tower_rotation_angle"
             || opt_key == "wipe_tower_bridging"
-            || opt_key == "wipe_tower_adhesion"
             || opt_key == "z_offset") {
             steps.emplace_back(psWipeTower);
         } else if (
@@ -1043,7 +1042,7 @@ void Print::_make_wipe_tower()
         float(this->config.wipe_tower_width.value), float(this->config.wipe_tower_per_color_wipe.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), bool(this->config.wipe_tower_adhesion),
+        float(this->config.wipe_tower_bridging),
         /*this->config.wipe_tower_advanced.value*/std::string(""),m_tool_ordering.first_extruder());
     
     //wipe_tower.set_retract();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index a09b896c9..ff426c9a2 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1840,13 +1840,6 @@ PrintConfigDef::PrintConfigDef()
     def->sidetext = L("mm");
     def->cli = "wipe-tower-bridging=f";
     def->default_value = new ConfigOptionFloat(10.);
-    
-    def = this->add("wipe_tower_adhesion", coBool);
-    def->label = L("Increase first layer adhesion");
-    def->tooltip = L("This prevents using sparse infill on the first layer, if it would be "
-                   "normally applied. Dense infill is used instead. ");
-    def->cli = "wipe-tower_adhesion!";
-    def->default_value = new ConfigOptionBool(true);
 
     def = this->add("xy_size_compensation", coFloat);
     def->label = L("XY Size Compensation");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index b35cf9cf1..d184a2161 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -629,7 +629,6 @@ public:
     ConfigOptionFloat               wipe_tower_per_color_wipe;
     ConfigOptionFloat               wipe_tower_rotation_angle;
     ConfigOptionFloat               wipe_tower_bridging;
-    ConfigOptionBool                wipe_tower_adhesion;
     ConfigOptionFloats              wiping_volumes_matrix;
     ConfigOptionFloats              wiping_volumes_extruders;
     ConfigOptionFloat               z_offset;
@@ -699,7 +698,6 @@ protected:
         OPT_PTR(wipe_tower_per_color_wipe);
         OPT_PTR(wipe_tower_rotation_angle);
         OPT_PTR(wipe_tower_bridging);
-        OPT_PTR(wipe_tower_adhesion);
         OPT_PTR(wiping_volumes_matrix);
         OPT_PTR(wiping_volumes_extruders);
         OPT_PTR(z_offset);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 847c7e523..99d003f7a 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -199,7 +199,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_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_adhesion", "wipe_tower_bridging",
+        "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_bridging",
         "wiping_volumes_matrix", "wiping_volumes_extruders", "compatible_printers", "compatible_printers_condition"
 
     };
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index d220130fd..e3e773f7d 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -520,7 +520,6 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_per_color_wipe");
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
         optgroup->append_single_option_line("wipe_tower_bridging");
-        optgroup->append_single_option_line("wipe_tower_adhesion");
         line = { _(L("Advanced")), "" };
         line.widget = [this](wxWindow* parent){
 			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _(L("Advanced settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);

From a923062167ed8f0632eda1eeca304e4aecaeec71 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 20 Mar 2018 13:50:51 +0100
Subject: [PATCH 043/118] Purging volume dialog improved and connected to
 configuration layer

---
 xs/src/slic3r/GUI/Tab.cpp             |  12 +-
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 175 +++++++++++++++++---------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp |  34 ++---
 3 files changed, 141 insertions(+), 80 deletions(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index e3e773f7d..146696d1e 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -527,12 +527,16 @@ void TabPrint::build()
 			sizer->Add(m_wipe_tower_btn);
 			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
 			{
-                std::vector<double> init_data = (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
-                WipingDialog dlg(this,std::vector<float>(init_data.begin(), init_data.end())); // dlg lives on stack, no need to call Destroy
+                std::vector<double> init_matrix = (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
+                std::vector<double> init_extruders = (m_config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
+
+                WipingDialog dlg(this,std::vector<float>(init_matrix.begin(),init_matrix.end()),std::vector<float>(init_extruders.begin(),init_extruders.end()));
 
 				if (dlg.ShowModal() == wxID_OK) {
-                    //load_key_value("wiping_volumes_matrix", dlg.GetValue());
-                    //std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
+                    std::vector<float> matrix = dlg.get_matrix();
+                    std::vector<float> extruders = dlg.get_extruders();
+                    (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
+                    (m_config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
                 }
 			}));
 			return sizer;
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 1c69285b7..fc3d30a5d 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -2,6 +2,11 @@
 #include <sstream>
 #include "WipeTowerDialog.hpp"
 
+//! macro used to mark string used at localization,
+//! return same string
+#define L(s) s
+
+
 
 RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
 : wxDialog(parent, -1,  wxT("Ramming customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
@@ -97,37 +102,12 @@ std::string RammingPanel::get_parameters()
 
 
 
-
-
-
-
-/*
-void WipingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
-    p.wipe_volumes.clear();
-    p.filament_wipe_volumes.clear();
-    for (int i=0;i<4;++i) {
-        // first go through the full matrix:
-        p.wipe_volumes.push_back(std::vector<float>());
-        for (int j=0;j<4;++j) {
-            double val = 0.;
-            edit_boxes[j][i]->GetValue().ToDouble(&val);
-            p.wipe_volumes[i].push_back((float)val);  
-        }
-        
-        // now the filament volumes:
-        p.filament_wipe_volumes.push_back(std::make_pair(m_old[i]->GetValue(),m_new[i]->GetValue()));
-    }
-}
-*/
-
-
-
-WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& init_data)
-: wxDialog(parent, -1,  wxT("Wiping customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders)
+: wxDialog(parent, -1,  wxT(L("Wipe tower - Purging volume adjustment")), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
     this->Centre();
     
-    m_panel_wiping  = new WipingPanel(this,init_data);
+    m_panel_wiping  = new WipingPanel(this,matrix,extruders);
     this->Show();
 
     auto main_sizer = new wxBoxSizer(wxVERTICAL);
@@ -140,59 +120,85 @@ WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& init_data)
     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
     
     this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
-        //m_output_data=read_dialog_values();
+        m_output_matrix    = m_panel_wiping->read_matrix_values();
+        m_output_extruders = m_panel_wiping->read_extruders_values();
         EndModal(wxID_OK);
         },wxID_OK);
 }
 
 
 
-WipingPanel::WipingPanel(wxWindow* parent,const std::vector<float>& data)
-: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
+
+WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders)
+: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(500,350),wxBORDER_RAISED)
 {
-    const int N = 4; // number of extruders
-    new wxStaticText(this,wxID_ANY,wxString("Volume to wipe when the filament is being"),wxPoint(40,55) ,wxSize(500,25));
-    new wxStaticText(this,wxID_ANY,wxString("unloaded"),wxPoint(110,75) ,wxSize(500,25));
-    new wxStaticText(this,wxID_ANY,wxString("loaded"),wxPoint(195,75) ,wxSize(500,25));        
-    m_widget_button = new wxButton(this,wxID_ANY,"-> Fill in the matrix ->",wxPoint(300,130),wxSize(175,50));
-    for (int i=0;i<N;++i) {
-        new wxStaticText(this,wxID_ANY,wxString("Filament #")<<i+1<<": ",wxPoint(20,105+30*i) ,wxSize(150,25),wxALIGN_LEFT);
-        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,data[2*i]));
-        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,data[2*i+1]));
+    m_number_of_extruders = (int)(sqrt(matrix.size())+0.001); // number of extruders
+    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),wxPoint(40,25) ,wxSize(500,35)));
+    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Volume to purge (mm\u00B3) when the filament is being")),wxPoint(40,85) ,wxSize(500,25)));
+    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("unloaded")),wxPoint(110,105) ,wxSize(500,25)));
+    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("loaded")),wxPoint(195,105) ,wxSize(500,25)));
+    m_widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxSize(170,20));
+
+    for (unsigned int i=0;i<m_number_of_extruders;++i) {
+        m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Tool #"))<<i+1<<": ",wxPoint(20,135+30*i) ,wxSize(150,25),wxALIGN_LEFT));
+        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,130+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i]));
+        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,130+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1]));
     }
-    
-    wxPoint origin(515,55);
-    for (int i=0;i<N;++i) {
+
+    wxPoint origin(50,85);
+    m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),wxPoint(40,25) ,wxSize(500,35)));
+    for (unsigned int i=0;i<m_number_of_extruders;++i) {
         edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
-        new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(45+60*i,25) ,wxSize(20,25));
-        new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(0,50+30*i) ,wxSize(500,25));
-        for (int j=0;j<N;++j) {
+        m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(45+60*i,25) ,wxSize(20,25)));
+        m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(0,50+30*i) ,wxSize(500,25)));
+        for (unsigned int j=0;j<m_number_of_extruders;++j) {
             edit_boxes.back().push_back(new wxTextCtrl(this,wxID_ANY,wxEmptyString,origin+wxPoint(25+60*i,45+30*j),wxSize(50,25)));
             if (i==j)
                 edit_boxes[i][j]->Disable();
             else
-                edit_boxes[i][j]->SetValue(wxString("")<<int(0));//p.wipe_volumes[j][i]));
+                edit_boxes[i][j]->SetValue(wxString("")<<int(matrix[m_number_of_extruders*j+i]));
         }
-        new wxStaticText(this,wxID_ANY,wxString("Filament changed to"),origin+wxPoint(75,0) ,wxSize(500,25));            
+        m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Filament changed to")),origin+wxPoint(75,0) ,wxSize(500,25)));
     }
-    
-    m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){fill_in_matrix();});
-    Refresh(this);
+
+    m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){toggle_advanced(true);});
+    toggle_advanced();
 }
 
+
+
+
+
+std::vector<float> WipingPanel::read_matrix_values() {
+    if (!m_advanced)
+        fill_in_matrix();
+    std::vector<float> output;
+    for (unsigned int i=0;i<m_number_of_extruders;++i) {
+        for (unsigned int j=0;j<m_number_of_extruders;++j) {
+            double val = 0.;
+            edit_boxes[j][i]->GetValue().ToDouble(&val);
+            output.push_back((float)val);
+        }
+    }
+    return output;
+}
+
+
+std::vector<float> WipingPanel::read_extruders_values() {
+    std::vector<float> output;
+    for (unsigned int i=0;i<m_number_of_extruders;++i) {
+        output.push_back(m_old[i]->GetValue());
+        output.push_back(m_new[i]->GetValue());
+    }
+    return output;
+}
+
+
 void WipingPanel::fill_in_matrix() {
-    wxArrayString choices;
-    choices.Add("sum");
-    choices.Add("maximum");
-    wxSingleChoiceDialog dialog(this,"How shall I calculate volume for any given pair?\n\nI can either sum volumes for old and new filament, or just use the higher value.","DEBUGGING",choices);
-    if (dialog.ShowModal() == wxID_CANCEL)
-        return;        
-    for (unsigned i=0;i<4;++i) {
-        for (unsigned j=0;j<4;++j) {
+    for (unsigned i=0;i<m_number_of_extruders;++i) {
+        for (unsigned j=0;j<m_number_of_extruders;++j) {
             if (i==j) continue;
-            if (!dialog.GetSelection()) edit_boxes[j][i]->SetValue(wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue()));
-            else
-              edit_boxes[j][i]->SetValue(wxString("")<< (std::max(m_old[i]->GetValue(), m_new[j]->GetValue())));
+                edit_boxes[j][i]->SetValue(wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue()));
         }
     }
 }
@@ -200,7 +206,52 @@ void WipingPanel::fill_in_matrix() {
 
 
 
+bool WipingPanel::advanced_matches_simple() {
+    for (unsigned i=0;i<m_number_of_extruders;++i) {
+        for (unsigned j=0;j<m_number_of_extruders;++j) {
+            if (i==j) continue;
+            if (edit_boxes[j][i]->GetValue() != (wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue())))
+                return false;
+        }
+    }
+    return true;
+}
 
 
 
+void WipingPanel::toggle_advanced(bool user_button) {
+    if (m_advanced && !advanced_matches_simple() && user_button) {
+        if (wxMessageDialog(this,wxString(L("Switching to simple settings will discard changes done in the advanced mode!\n\nDo you want to proceed?")),
+                            wxString(L("Warning")),wxYES_NO|wxICON_EXCLAMATION).ShowModal() != wxID_YES)
+            return;
+    }
 
+    m_advanced = !m_advanced;
+
+    if (!user_button) {                 // we were called from constructor
+        if (advanced_matches_simple())  // advanced and simple match - let's show simple version
+            m_advanced = false;
+        else
+            m_advanced = true;
+    }
+
+    for (unsigned i=0;i<m_number_of_extruders;++i) {        // shows/hides input controls
+            for (unsigned j=0;j<m_number_of_extruders;++j)
+                edit_boxes[i][j]->Show(m_advanced);
+            m_old[i]->Show(!m_advanced);
+            m_new[i]->Show(!m_advanced);
+    }
+    for (const auto& widget : m_advanced_widgets)          // shows/hides other widgets
+        widget->Show(m_advanced);
+    for (const auto& widget : m_notadvanced_widgets)
+        widget->Show(!m_advanced);
+
+    if (m_advanced) {
+        if (user_button) fill_in_matrix();  // otherwise keep values loaded from config
+        m_widget_button->SetLabel(L("Show simplified settings"));
+    }
+    else
+        m_widget_button->SetLabel(L("Show advanced settings"));
+
+    this->Refresh();
+}
\ No newline at end of file
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index 8e84f3dc1..0adefe5f2 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -46,18 +46,28 @@ private:
 
 
 
+
+
+
 class WipingPanel : public wxPanel {
 public:
-    WipingPanel(wxWindow* parent,const std::vector<float>& data);
-    void fill_parameters(Slic3r::WipeTowerParameters& p);
+    WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders);
+    std::vector<float> read_matrix_values();
+    std::vector<float> read_extruders_values();
         
 private:
     void fill_in_matrix();
+    void toggle_advanced(bool user_button = false);
+    bool advanced_matches_simple();
         
     std::vector<wxSpinCtrl*> m_old;
     std::vector<wxSpinCtrl*> m_new;
+    std::vector<wxWindow*>   m_advanced_widgets;
+    std::vector<wxWindow*>   m_notadvanced_widgets;
     std::vector<std::vector<wxTextCtrl*>> edit_boxes;
-    wxButton* m_widget_button=nullptr;    
+    wxButton* m_widget_button           = nullptr;
+    unsigned int m_number_of_extruders  = 0;
+    bool m_advanced                     = false;
 };
 
 
@@ -66,19 +76,15 @@ private:
 
 class WipingDialog : public wxDialog {
 public:
-    WipingDialog(wxWindow* parent,const std::vector<float>& init_data);
-    std::vector<float> get_value() const { return m_output_data; }
-    
-    
+    WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders);
+    std::vector<float> get_matrix() const    { return m_output_matrix; }
+    std::vector<float> get_extruders() const { return m_output_extruders; }
+
+
 private:
     WipingPanel*  m_panel_wiping  = nullptr;
-    std::vector<float> m_output_data;
-            
-    std::string read_dialog_values() {
-        Slic3r::WipeTowerParameters p;
-        m_panel_wiping ->fill_parameters(p);
-        //return p.to_string();
-    }
+    std::vector<float> m_output_matrix;
+    std::vector<float> m_output_extruders;
 };
 
 #endif  // _WIPE_TOWER_DIALOG_H_
\ No newline at end of file

From a782424d5f4ab4e34e4291af8cee965b2ad23848 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 20 Mar 2018 15:07:18 +0100
Subject: [PATCH 044/118] Wipe tower generator connected to purging volumes
 from the configuration layer

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp |  8 +--
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 63 ++-------------------
 xs/src/libslic3r/Print.cpp                  |  4 +-
 xs/src/slic3r/GUI/WipeTowerDialog.hpp       |  5 --
 4 files changed, 13 insertions(+), 67 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 59fa7dced..2010b7ae7 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -514,7 +514,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
         toolchange_Load(writer, cleaning_box); // Prime the tool.
         if (idx_tool + 1 == tools.size()) {
             // Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
-            toolchange_Wipe(writer, cleaning_box, m_par.wipe_volumes[tools[idx_tool-1]][tool]);
+            toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool]);
         } else {
             // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
             //writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
@@ -566,7 +566,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 = m_par.wipe_volumes[b.old_tool][b.new_tool];
+				wipe_volume = wipe_volumes[b.old_tool][b.new_tool];
 				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;
@@ -1100,7 +1100,7 @@ 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(m_par.wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
+    length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
     length_to_extrude = std::max(length_to_extrude,0.f);
 
 	depth += (int(length_to_extrude / width) + 1) * m_line_width;
@@ -1146,7 +1146,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(m_par.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(wipe_volumes[m_layer_info->tool_changes.back().old_tool][m_layer_info->tool_changes.back().new_tool],
                               m_line_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 8cfc5872c..acdcef8f2 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -31,59 +31,6 @@ namespace PrusaMultiMaterial {
 
 
 
-// Operator overload to output std::pairs
-template <typename T>
-std::ostream& operator<<(std::ostream& stream,const std::pair<T,T>& pair) {
-    return stream << pair.first << " " << pair.second;
-}
-
-// Operator overload to output elements of a vector to std::ofstream easily:
-template <typename T>
-std::ostream& operator<<(std::ostream& stream,const std::vector<T>& vect) {
-    for (const auto& element : vect)
-        stream << element << " ";
-    return stream;
-}
-
-// Operator overload to input elements of a vector from std::ifstream easily (reads until a fail)
-template <typename T>
-std::istream& operator>>(std::istream& stream, std::vector<T>& vect) {
-    vect.clear();
-    T value{};
-    bool we_read_something = false;
-    while (stream >> value) {
-        vect.push_back(value);
-        we_read_something = true;
-    }
-    if (!stream.eof() && we_read_something) { // if this is not eof, we might be at separator - let's get rid of it
-        stream.clear();     // if we failed on very first line or reached eof, return stream in good() state
-        stream.get();       // get() whatever we are stuck at
-    }
-    return stream;
-}
-
-
-// This struct is used to store parameters and to pass it to wipe tower generator
-struct WipeTowerParameters {
-    WipeTowerParameters() {  }           // create new empty object
-    WipeTowerParameters(const std::string& init_data) { // create object and initialize from std::string
-        set_defaults();
-    }
-    
-    void set_defaults() {
-        sampling = 0.25f;
-        wipe_volumes = {{  0.f, 60.f, 60.f, 60.f},
-                        { 60.f,  0.f, 60.f, 60.f},
-                        { 60.f, 60.f,  0.f, 60.f},
-                        { 60.f, 60.f, 60.f,  0.f}};
-        filament_wipe_volumes = {{30.f,30.f},{30.f,30.f},{30.f,30.f},{30.f,30.f}};
-    }
-    
-    float sampling = 0.25f; // this does not quite work yet, keep it fixed to 0.25f
-    std::vector<std::vector<float>> wipe_volumes;
-    std::vector<std::pair<int,int>> filament_wipe_volumes;
-};
-
 
 class WipeTowerPrusaMM : public WipeTower
 {
@@ -110,7 +57,7 @@ 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 wipe_area, float rotation_angle, float cooling_tube_retraction,
-                     float cooling_tube_length, float parking_pos_retraction, float bridging, const std::string& parameters,
+                     float cooling_tube_length, float parking_pos_retraction, float bridging, const std::vector<float>& wiping_matrix,
                      unsigned int initial_tool) :
 		m_wipe_tower_pos(x, y),
 		m_wipe_tower_width(width),
@@ -123,9 +70,11 @@ public:
         m_cooling_tube_length(cooling_tube_length),
         m_parking_pos_retraction(parking_pos_retraction),
 		m_current_tool(initial_tool),
-        m_par(parameters) 
+        m_bridging(bridging)
  	{
-        m_bridging = bridging;
+        const unsigned int number_of_extruders = 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));
         
 		for (size_t i = 0; i < 4; ++ i) {
 			// Extruder specific parameters.
@@ -301,7 +250,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;
-    WipeTowerParameters m_par;
+    std::vector<std::vector<float>> wipe_volumes;
 
 	float m_depth_traversed = 0.f; // Current y position at the wipe tower.
 	// How much to wipe the 1st extruder over the wipe tower at the 1st layer
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 0167b8ba7..acff4492a 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 == "wipe_tower_per_color_wipe"
             || opt_key == "wipe_tower_rotation_angle"
             || opt_key == "wipe_tower_bridging"
+            || opt_key == "wiping_volumes_matrix"
             || opt_key == "z_offset") {
             steps.emplace_back(psWipeTower);
         } else if (
@@ -1043,7 +1044,8 @@ void Print::_make_wipe_tower()
         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),
-        /*this->config.wipe_tower_advanced.value*/std::string(""),m_tool_ordering.first_extruder());
+        std::vector<float>((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end()),
+        m_tool_ordering.first_extruder());
     
     //wipe_tower.set_retract();
     //wipe_tower.set_zhop();
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index 0adefe5f2..54ae9596d 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -5,16 +5,11 @@
 #include <wx/stattext.h>
 #include <wx/textctrl.h>
 #include <wx/checkbox.h>
-#include <wx/choicdlg.h>
-#include <wx/notebook.h>
 #include <wx/msgdlg.h>
 
-#include "../../libslic3r/GCode/WipeTowerPrusaMM.hpp"
 #include "RammingChart.hpp"
 
 
-std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par);
-
 class RammingPanel : public wxPanel {
 public:
     RammingPanel(wxWindow* parent);

From 0fc2da5a325d07252d57a6bb1a88ad1e27824df3 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 20 Mar 2018 15:45:11 +0100
Subject: [PATCH 045/118] Wipe tower generator should now work for more than 4
 extruders (actual number extracted from wiping_volumes_matrix)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 32 +++++++++++----------
 xs/src/libslic3r/Print.cpp                  | 11 +++----
 2 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index acdcef8f2..90317673e 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -69,19 +69,19 @@ public:
         m_cooling_tube_retraction(cooling_tube_retraction),
         m_cooling_tube_length(cooling_tube_length),
         m_parking_pos_retraction(parking_pos_retraction),
-		m_current_tool(initial_tool),
-        m_bridging(bridging)
+		m_bridging(bridging),
+        m_current_tool(initial_tool)
  	{
         const unsigned int number_of_extruders = 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));
         
-		for (size_t i = 0; i < 4; ++ i) {
+		/*for (size_t i = 0; i < number_of_extruders; ++ i) {
 			// Extruder specific parameters.
-			m_filpar[i].material = PLA;
+            m_filpar[i].material = PLA;
 			m_filpar[i].temperature = 0;
 			m_filpar[i].first_layer_temperature = 0;
-		}
+		}*/
 	}
 
 	virtual ~WipeTowerPrusaMM() {}
@@ -97,6 +97,8 @@ public:
 	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
                       float unloading_speed, float delay, int cooling_time, std::string ramming_parameters)
 	{
+        while (m_filpar.size() < idx+1)   // makes sure the required element is in the vector
+            m_filpar.push_back(FilamentParameters());
         m_filpar[idx].material = material;
         m_filpar[idx].temperature = temp;
         m_filpar[idx].first_layer_temperature = first_layer_temp;
@@ -226,20 +228,20 @@ private:
 
 
     struct FilamentParameters {
-        material_type 	    material;
-        int  			    temperature;
-        int  			    first_layer_temperature;
-        float               loading_speed;
-        float               unloading_speed;
-        float               delay;
-        int                 cooling_time;
-        float               ramming_line_width_multiplicator;
-        float               ramming_step_multiplicator;
+        material_type 	    material = PLA;
+        int  			    temperature = 0;
+        int  			    first_layer_temperature = 0;
+        float               loading_speed = 0.f;
+        float               unloading_speed = 0.f;
+        float               delay = 0.f ;
+        int                 cooling_time = 0;
+        float               ramming_line_width_multiplicator = 0.f;
+        float               ramming_step_multiplicator = 0.f;
         std::vector<float>  ramming_speed;
     };
 
 	// Extruder specific parameters.
-    FilamentParameters m_filpar[4];
+    std::vector<FilamentParameters> m_filpar;
 
 
 	// State of the wiper tower generator.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index acff4492a..cd227ed1c 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1037,21 +1037,22 @@ 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_per_color_wipe.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),
-        std::vector<float>((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end()),
-        m_tool_ordering.first_extruder());
-    
+        float(this->config.wipe_tower_bridging), wiping_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 < 4; ++ i)
+    for (size_t i = 0; i < (int)(sqrt(wiping_volumes.size())+EPSILON); ++ i)
         wipe_tower.set_extruder(
             i, 
             WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),

From ac9db81820af7b531ea548cc8f8f2b149a87db08 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 21 Mar 2018 09:36:27 +0100
Subject: [PATCH 046/118] Normalization of purging volume parameters when
 number of extruders is changed

---
 xs/src/slic3r/GUI/PresetBundle.cpp | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 668b9ee12..d26a007bb 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -719,6 +719,35 @@ void PresetBundle::update_multi_material_filament_presets()
     // Append the rest of filament presets.
 //    if (this->filament_presets.size() < num_extruders)
         this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back());
+
+
+
+    // Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator):
+    std::vector<double> old_matrix = (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
+    size_t old_number_of_extruders = int(sqrt(old_matrix.size())+EPSILON);
+    // First do it for the extruders presets (in-place):
+            std::vector<double>& extruders = (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
+            while (extruders.size() < 2*num_extruders) {
+                extruders.push_back(extruders[0]);  // copy the values from the first extruder
+                extruders.push_back(extruders[1]);
+            }
+            while (extruders.size() > 2*num_extruders) {
+                extruders.pop_back();
+                extruders.pop_back();
+            }
+    // Now update the purging volume matrix:
+    if (num_extruders != old_number_of_extruders) {
+        std::vector<double> new_matrix;
+        for (unsigned int i=0;i<num_extruders;++i)
+            for (unsigned int j=0;j<num_extruders;++j) {
+                // append the value for this pair from the old matrix (if it's there):
+                if (i<old_number_of_extruders && j<old_number_of_extruders)
+                    new_matrix.push_back(old_matrix[i*old_number_of_extruders + j]);
+                else
+                    new_matrix.push_back( i==j ? 0. : extruders[2*i]+extruders[2*j+1]); // so it matches new extruder volumes
+            }
+        (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = new_matrix;
+    }
 }
 
 void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible)

From 1a1be94c99f99a63aab45a13f83711e8a007e8c2 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 21 Mar 2018 10:20:36 +0100
Subject: [PATCH 047/118] Purging volume parameters normalization - minor
 changes

---
 xs/src/slic3r/GUI/PresetBundle.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index d26a007bb..1d4795832 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -721,22 +721,21 @@ void PresetBundle::update_multi_material_filament_presets()
         this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back());
 
 
-
     // Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator):
     std::vector<double> old_matrix = (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
     size_t old_number_of_extruders = int(sqrt(old_matrix.size())+EPSILON);
-    // First do it for the extruders presets (in-place):
+    if (num_extruders != old_number_of_extruders) {
+            // First verify if purging volumes presets for each extruder matches number of extruders
             std::vector<double>& extruders = (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
             while (extruders.size() < 2*num_extruders) {
-                extruders.push_back(extruders[0]);  // copy the values from the first extruder
-                extruders.push_back(extruders[1]);
+                extruders.push_back(extruders.size()>1 ? extruders[0] : 50.);  // copy the values from the first extruder
+                extruders.push_back(extruders.size()>1 ? extruders[1] : 50.);
             }
             while (extruders.size() > 2*num_extruders) {
                 extruders.pop_back();
                 extruders.pop_back();
             }
-    // Now update the purging volume matrix:
-    if (num_extruders != old_number_of_extruders) {
+
         std::vector<double> new_matrix;
         for (unsigned int i=0;i<num_extruders;++i)
             for (unsigned int j=0;j<num_extruders;++j) {

From 9519fae49072955eaf793195132e8f717ee16a23 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 21 Mar 2018 15:57:17 +0100
Subject: [PATCH 048/118] Partial refactoring of wipe tower code (got rid of
 global constants, etc)

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 108 ++++++++------------
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  94 +++++++----------
 xs/src/libslic3r/Print.cpp                  |   1 -
 xs/src/slic3r/GUI/WipeTowerDialog.cpp       |   2 +-
 4 files changed, 79 insertions(+), 126 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 2010b7ae7..7c8181ff6 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -33,7 +33,6 @@ TODO LIST
 #define strcasecmp _stricmp
 #endif
 
-const bool  peters_wipe_tower = false;	// sparse wipe tower inspired by Peter's post processor - not finished yet
 
 namespace Slic3r
 {
@@ -119,7 +118,7 @@ public:
 		if (! m_preview_suppressed && e > 0.f && len > 0.) {
 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
 			// This is left zero if it is a travel move.
-			float width = float(double(e) * Filament_Area / (len * m_layer_height));
+			float width = float(double(e) * /*Filament_Area*/2.40528 / (len * m_layer_height));
 			// Correct for the roundings of a squished extrusion.
 			width += m_layer_height * float(1. - M_PI / 4.);
 			if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
@@ -415,33 +414,12 @@ private:
 	}
 
 	Writer& operator=(const Writer &rhs);
-};
-
-/*
-class Material
-{
-public:
-	std::string 				name;
-	std::string 				type;
-
-	struct RammingStep {
-//		float length;
-		float extrusion_multiplier; // sirka linky
-		float extrusion;
-		float speed;
-	};
-	std::vector<RammingStep> 			ramming_sequence;
-
-	// Number and speed of the cooling moves.
-	std::vector<float>					cooling_moves;
-
-	// Percentage of the speed overide, in pairs of <z, percentage>
-	std::vector<std::pair<float, int>> 	speed_override;
-};
-*/
+}; // class Writer
 
 }; // namespace PrusaMultiMaterial
 
+
+
 WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name)
 {
 	if (strcasecmp(name, "PLA") == 0)
@@ -476,6 +454,7 @@ 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_num_layer_changes 	= 0;
 	this->m_current_tool 		= tools.front();
@@ -484,8 +463,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
     // Due to the XYZ calibration, this working space may shrink slightly from all directions,
     // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
 //	box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
-	//FIXME: set the width properly
-	const float prime_section_width = 55.f;
+
+	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;
@@ -556,7 +535,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 	if ( m_print_brim )
 		return toolchange_Brim();
 
-	
 	float wipe_area = 0.f;
 	bool last_change_in_layer = false;
 	float wipe_volume = 0.f;
@@ -589,7 +567,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.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 && !peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
+		.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))
 		.append(";--------------------\n"
 				"; CP TOOLCHANGE START\n")
 		.comment_with_value(" toolchange #", m_num_tool_changes)
@@ -625,7 +603,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 
     if (last_change_in_layer) {// draw perimeter line
         writer.set_y_shift(m_y_shift);
-        if (peters_wipe_tower)
+        if (m_peters_wipe_tower)
             writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
         else {
             writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
@@ -724,7 +702,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	
 	writer.append("; CP TOOLCHANGE UNLOAD\n");
 	
-	const float line_width = m_line_width * m_filpar[m_current_tool].ramming_line_width_multiplicator;       // desired ramming line thickness
+	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
 
 	unsigned i = 0;										// iterates through ramming_speed
@@ -757,7 +735,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
             if (tch.old_tool == m_current_tool) {
                 sum_of_depths += tch.ramming_depth;
                 float ramming_end_y = m_wipe_tower_pos.y + sum_of_depths;
-                ramming_end_y -= (y_step/m_extra_spacing-m_line_width) / 2.f;   // center of final ramming line
+                ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f;   // center of final ramming line
 
                 // debugging:
                 /*float oldx = writer.x();
@@ -851,7 +829,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 
 	// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
     // the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
-	writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_line_width) / 2.f + m_perimeter_width, 2400.f);
+	writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f);
 
 	writer.resume_preview()
 		  .flush_planner_queue();
@@ -925,8 +903,8 @@ void WipeTowerPrusaMM::toolchange_Wipe(
     //   the ordered volume, even if it means violating the box. This can later be removed and simply
     // wipe until the end of the assigned area.
 
-	float x_to_wipe = volume_to_length(wipe_volume, m_line_width, m_layer_height);
-	float dy = m_extra_spacing*m_line_width;
+	float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height);
+	float dy = m_extra_spacing*m_perimeter_width;
 	float wipe_speed = 1600.f;
 
     // if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway)
@@ -990,7 +968,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 		.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 && !peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
+		.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
 		.append(";--------------------\n"
 				"; CP EMPTY GRID START\n")
 		// m_num_layer_changes is incremented by set_z, so it is 1 based.
@@ -1024,11 +1002,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
     if (m_is_first_layer && m_adhesion) {
         // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
         box.expand(-m_perimeter_width/2.f);
-        unsigned nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
+        int nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
         float step   = (box.lu.y - box.ld.y) / nsteps;
         writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f));
         if (nsteps >= 0)
-            for (size_t i = 0; i < nsteps; ++i)	{
+            for (int i = 0; i < nsteps; ++i)	{
                 writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step);
                 writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y());
                 writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
@@ -1076,7 +1054,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)
-{	
+{
 	assert(m_plan.back().z <= z_par + WT_EPSILON );	// refuses to add a layer below the last one
 
 	if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan first
@@ -1094,16 +1072,16 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
 	float depth = 0.f;			
 	float width = m_wipe_tower_width - 3*m_perimeter_width; 
 	float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
-										m_line_width * m_filpar[old_tool].ramming_line_width_multiplicator,
+										m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
 										layer_height_par);
-	depth = (int(length_to_extrude / width) + 1) * (m_line_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator);
+	depth = (int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator);
     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_line_width, layer_height_par);
+    length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_perimeter_width, layer_height_par);
     length_to_extrude = std::max(length_to_extrude,0.f);
 
-	depth += (int(length_to_extrude / width) + 1) * m_line_width;
+	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));
@@ -1147,12 +1125,12 @@ 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],
-                              m_line_width,m_layer_info->height)  - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
+                              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);
-        float depth_to_wipe = m_line_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) ) * m_extra_spacing;
+        float depth_to_wipe = m_perimeter_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) ) * m_extra_spacing;
 
-        //depth += (int(length_to_extrude / width) + 1) * m_line_width;
+        //depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
         m_layer_info->tool_changes.back().required_depth = m_layer_info->tool_changes.back().ramming_depth + depth_to_wipe;
     }
 }
@@ -1172,7 +1150,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
         plan_tower();
     }
 
-	if (peters_wipe_tower)
+	if (m_peters_wipe_tower)
 			make_wipe_tower_square();
 
     m_layer_info = m_plan.begin();
@@ -1183,35 +1161,33 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 		set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
 
 
-		if (peters_wipe_tower)
+		if (m_peters_wipe_tower)
 			m_wipe_tower_rotation_angle += 90.f;
 		else
             m_wipe_tower_rotation_angle += 180.f;
 
-		if (!peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
+		if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
 			m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
 
 		for (const auto &toolchange : layer.tool_changes)
 			layer_result.emplace_back(tool_change(toolchange.new_tool, false));
 
 		if (! layer_finished()) {
-			layer_result.emplace_back(finish_layer());
-			if (layer_result.size() > 1) {
-				// Merge the two last tool changes into one.
-				WipeTower::ToolChangeResult &tc1 = layer_result[layer_result.size() - 2];
-				WipeTower::ToolChangeResult &tc2 = layer_result.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;
+            auto finish_layer_toolchange = finish_layer();
+            if (layer_result.size() > 1) { // we will merge it to the last toolchange
+                auto& last_toolchange = layer_result.back();
+                if (last_toolchange.end_pos != finish_layer_toolchange.start_pos) {
+                    char buf[2048];     // Add a travel move from tc1.end_pos to tc2.start_pos.
+					sprintf(buf, "G1 X%.3f Y%.3f F7200\n", finish_layer_toolchange.start_pos.x, finish_layer_toolchange.start_pos.y);
+					last_toolchange.gcode += buf;
 				}
-				tc1.gcode += tc2.gcode;
-				tc1.extrusions.insert(tc1.extrusions.end(), tc2.extrusions.begin(), tc2.extrusions.end());
-				tc1.end_pos = tc2.end_pos;
-				layer_result.pop_back();
-			}			
-		}
+                last_toolchange.gcode += finish_layer_toolchange.gcode;
+                last_toolchange.extrusions.insert(last_toolchange.extrusions.end(),finish_layer_toolchange.extrusions.begin(),finish_layer_toolchange.extrusions.end());
+                last_toolchange.end_pos = finish_layer_toolchange.end_pos;
+            }
+            else
+                layer_result.emplace_back(std::move(finish_layer_toolchange));
+        }
 
 		result.emplace_back(std::move(layer_result));
 		m_is_first_layer = false;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 90317673e..734d61dc6 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -8,19 +8,6 @@
 
 #include "WipeTower.hpp"
 
-// Following is used to calculate extrusion flow - should be taken from config in future
-const float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^3
-const float Nozzle_Diameter = 0.4f;	// nozzle diameter in mm
-// desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
-const float Width_To_Nozzle_Ratio = 1.25f;
-
-// m_perimeter_width was hardcoded until now as 0.5 (for 0.4 nozzle and 0.2 layer height)
-// FIXME m_perimeter_width is used in plan_toolchange - take care of proper initialization value when changing to variable
-const float Konst = 1.f;
-const float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Konst;
-
-const float WT_EPSILON = 1e-3f;
-
 
 namespace Slic3r
 {
@@ -31,7 +18,6 @@ namespace PrusaMultiMaterial {
 
 
 
-
 class WipeTowerPrusaMM : public WipeTower
 {
 public:
@@ -65,40 +51,27 @@ public:
 		m_y_shift(0.f),
 		m_z_pos(0.f),
 		m_is_first_layer(false),
-		m_is_last_layer(false),
         m_cooling_tube_retraction(cooling_tube_retraction),
         m_cooling_tube_length(cooling_tube_length),
         m_parking_pos_retraction(parking_pos_retraction),
 		m_bridging(bridging),
         m_current_tool(initial_tool)
  	{
-        const unsigned int number_of_extruders = int(sqrt(wiping_matrix.size())+WT_EPSILON);
+        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));
-        
-		/*for (size_t i = 0; i < number_of_extruders; ++ i) {
-			// Extruder specific parameters.
-            m_filpar[i].material = PLA;
-			m_filpar[i].temperature = 0;
-			m_filpar[i].first_layer_temperature = 0;
-		}*/
 	}
 
 	virtual ~WipeTowerPrusaMM() {}
 
-	// _retract - retract value in mm
-	void set_retract(float retract) { m_retract = retract; }
-	
-	// _zHop - z hop value in mm
-	void set_zhop(float zhop) { m_zhop = zhop; }
-
 
 	// 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, int cooling_time, std::string ramming_parameters)
 	{
-        while (m_filpar.size() < idx+1)   // makes sure the required element is in the vector
-            m_filpar.push_back(FilamentParameters());
+        //while (m_filpar.size() < idx+1)   // makes sure the required element is in the vector
+        m_filpar.push_back(FilamentParameters());
+
         m_filpar[idx].material = material;
         m_filpar[idx].temperature = temp;
         m_filpar[idx].first_layer_temperature = first_layer_temp;
@@ -106,7 +79,7 @@ public:
         m_filpar[idx].unloading_speed = unloading_speed;
         m_filpar[idx].delay = delay;
         m_filpar[idx].cooling_time = cooling_time;
-        
+
         std::stringstream stream{ramming_parameters};
         float speed = 0.f;
         stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
@@ -117,21 +90,14 @@ public:
 	}
 
 
-	// Setter for internal structure m_plan containing info about the future wipe tower
+	// 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);
 
 	// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
 	void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
 
-	// Calculates depth for all layers and propagates them downwards
-	void plan_tower();
 
-	// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
-	void make_wipe_tower_square();
-
-    // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
-    void save_on_last_wipe();
 
 	// Switch to a next layer.
 	virtual void set_layer(
@@ -150,15 +116,16 @@ public:
 		m_layer_height			= layer_height;
 		m_is_first_layer 		= is_first_layer;
 		m_print_brim = is_first_layer;
-		m_depth_traversed  = 0.f; // to make room for perimeter line
+		m_depth_traversed  = 0.f;
 		m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
 		
 		++ m_num_layer_changes;
 		
-		// Calculates extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height
+		// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
 		m_extrusion_flow = extrusion_flow(layer_height);
 
-		while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1!=m_plan.end())
+        // Advance m_layer_info iterator, making sure we got it right
+		while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
 			++m_layer_info;
 	}
 
@@ -183,7 +150,7 @@ public:
 	// On the first layer, extrude a brim around the future wipe tower first.
 	virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
 
-	// Fill the unfilled space with a zig-zag.
+	// Fill the unfilled space with a sparse infill.
 	// Call this method only if layer_finished() is false.
 	virtual ToolChangeResult finish_layer();
 
@@ -196,34 +163,38 @@ public:
 private:
 	WipeTowerPrusaMM();
 
-	enum wipe_shape // A fill-in direction (positive Y, negative Y) alternates with each layer.
+	enum wipe_shape // A fill-in direction
 	{
 		SHAPE_NORMAL = 1,
 		SHAPE_REVERSED = -1
 	};
 
+
+    const bool  m_peters_wipe_tower   = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
+    const float Filament_Area         = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^3
+    const float Nozzle_Diameter       = 0.4f;  // nozzle diameter in mm
+    const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
+    const float WT_EPSILON            = 1e-3f;
+
+
 	xy 	   m_wipe_tower_pos; 			// Left front corner of the wipe tower in mm.
 	float  m_wipe_tower_width; 			// Width of the wipe tower.
 	float  m_wipe_tower_depth 	= 0.f; 	// Depth of the wipe tower
-	float  m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis
+	float  m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
 	float  m_y_shift			= 0.f;  // y shift passed to writer
 	float  m_z_pos 				= 0.f;  // Current Z position.
 	float  m_layer_height 		= 0.f; 	// Current layer height.
 	size_t m_max_color_changes 	= 0; 	// Maximum number of color changes per layer.
 	bool   m_is_first_layer 	= false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
-	bool   m_is_last_layer 		= false;// Is this the last layer of this waste tower?
-    bool   m_layer_parity       = false;
 
 	// G-code generator parameters.
-	float  			m_zhop 			 = 0.5f;
-	float  			m_retract		 = 4.f;
     float           m_cooling_tube_retraction   = 0.f;
     float           m_cooling_tube_length       = 0.f;
     float           m_parking_pos_retraction    = 0.f;
     float           m_bridging                  = 0.f;
     bool            m_adhesion                  = true;
 
-	float m_line_width = Nozzle_Diameter * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
+	float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
 	float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
 
 
@@ -244,7 +215,7 @@ private:
     std::vector<FilamentParameters> m_filpar;
 
 
-	// State of the wiper tower generator.
+	// State of the wipe tower generator.
 	unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
 	unsigned int m_num_tool_changes  = 0; // Tool change change counter for the output statistics.
 	///unsigned int 	m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
@@ -254,12 +225,10 @@ private:
 	unsigned int 	m_current_tool  = 0;
     std::vector<std::vector<float>> wipe_volumes;
 
-	float m_depth_traversed = 0.f; // Current y position at the wipe tower.
-	// How much to wipe the 1st extruder over the wipe tower at the 1st layer
-	// after the wipe tower brim has been extruded?
-	float  			m_initial_extra_wipe = 0.f;
-	bool 			m_left_to_right = true;
-	float			m_extra_spacing = 1.f;
+	float           m_depth_traversed = 0.f; // Current y position at the wipe tower.
+	bool 			m_left_to_right   = true;
+	float			m_extra_spacing   = 1.f;
+
 
 	// Calculates extrusion flow needed to produce required line width for given layer height
 	float extrusion_flow(float layer_height = -1.f) const	// negative layer_height - return current m_extrusion_flow
@@ -274,6 +243,15 @@ private:
 		return volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.)));
 	}
 
+	// Calculates depth for all layers and propagates them downwards
+	void plan_tower();
+
+	// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
+	void make_wipe_tower_square();
+
+    // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
+    void save_on_last_wipe();
+
 
 	struct box_coordinates
 	{
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index cd227ed1c..18d2bcead 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -1075,7 +1075,6 @@ void Print::_make_wipe_tower()
 
     // 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
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index fc3d30a5d..ac35756a4 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -40,7 +40,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
 {
     new wxStaticText(this,wxID_ANY,wxString("Total ramming time (s):"),     wxPoint(500,105),      wxSize(200,25),wxALIGN_LEFT);
     m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,       wxPoint(700,100),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0.,5.0,3.,0.5);        
-    new wxStaticText(this,wxID_ANY,wxString("Total rammed volume (mm3):"),  wxPoint(500,135),      wxSize(200,25),wxALIGN_LEFT);
+    new wxStaticText(this,wxID_ANY,wxString("Total rammed volume (mm\u00B3):"),  wxPoint(500,135),      wxSize(200,25),wxALIGN_LEFT);
     m_widget_volume = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,           wxPoint(700,130),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,10000,0);        
     new wxStaticText(this,wxID_ANY,wxString("Ramming line width (%):"),     wxPoint(500,205),      wxSize(200,25),wxALIGN_LEFT);
     m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,       wxPoint(700,200),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        

From e7a5bc1afefb3d051c888830e3655e8e456b6eaa Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 21 Mar 2018 16:01:31 +0100
Subject: [PATCH 049/118] Added a check that no object uses extruder the
 printer doesn't have

---
 xs/src/libslic3r/Print.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 18d2bcead..62c9c8819 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -624,7 +624,12 @@ std::string Print::validate() const
         for (unsigned int extruder_id : extruders)
             nozzle_diameters.push_back(this->config.nozzle_diameter.get_at(extruder_id));
         double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end());
-        
+
+        unsigned int total_extruders_count = this->config.nozzle_diameter.size();
+        for (const auto& extruder_idx : extruders)
+            if ( extruder_idx >= total_extruders_count )
+                return "One or more object have assigned an extruder that the printer does not have.";
+
         for (PrintObject *object : this->objects) {
             if ((object->config.support_material_extruder == -1 || object->config.support_material_interface_extruder == -1) &&
                 (object->config.raft_layers > 0 || object->config.support_material.value)) {

From 036e41ae6952b84c32b93b9a2ce21741542cf7ea Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 22 Mar 2018 13:07:45 +0100
Subject: [PATCH 050/118] Wiping dialog - first experiments with sizers

---
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 138 +++++++++++++++-----------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp |   8 +-
 2 files changed, 84 insertions(+), 62 deletions(-)

diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index ac35756a4..1989a82f1 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -2,6 +2,8 @@
 #include <sstream>
 #include "WipeTowerDialog.hpp"
 
+#include <wx/sizer.h>
+
 //! macro used to mark string used at localization,
 //! return same string
 #define L(s) s
@@ -22,9 +24,9 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
     SetSizer(main_sizer);
     SetMinSize(GetSize());
     main_sizer->SetSizeHints(this);
-  
+
     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
-    
+
     this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
         m_output_data = m_panel_ramming->get_parameters();
         EndModal(wxID_OK);
@@ -101,74 +103,105 @@ std::string RammingPanel::get_parameters()
 
 
 
-
+// Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode:
 WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders)
 : wxDialog(parent, -1,  wxT(L("Wipe tower - Purging volume adjustment")), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
     this->Centre();
-    
-    m_panel_wiping  = new WipingPanel(this,matrix,extruders);
-    this->Show();
+
+    auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize);
+    m_panel_wiping  = new WipingPanel(this,matrix,extruders, widget_button);
 
     auto main_sizer = new wxBoxSizer(wxVERTICAL);
     main_sizer->Add(m_panel_wiping, 1, wxEXPAND);
+
+
+    main_sizer->Add(widget_button,0,wxALIGN_CENTER_HORIZONTAL|wxCENTER,10);
     main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
     SetSizer(main_sizer);
     SetMinSize(GetSize());
     main_sizer->SetSizeHints(this);
-    
+
     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
     
-    this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
-        m_output_matrix    = m_panel_wiping->read_matrix_values();
-        m_output_extruders = m_panel_wiping->read_extruders_values();
+    this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {                 // if OK button is clicked..
+        m_output_matrix    = m_panel_wiping->read_matrix_values();    // ..query wiping panel and save returned values
+        m_output_extruders = m_panel_wiping->read_extruders_values(); // so they can be recovered later by calling get_...()
         EndModal(wxID_OK);
         },wxID_OK);
+
+    this->Show();
 }
 
 
 
-
-WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders)
+// This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
+WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button)
 : wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(500,350),wxBORDER_RAISED)
 {
-    m_number_of_extruders = (int)(sqrt(matrix.size())+0.001); // number of extruders
-    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),wxPoint(40,25) ,wxSize(500,35)));
-    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Volume to purge (mm\u00B3) when the filament is being")),wxPoint(40,85) ,wxSize(500,25)));
-    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("unloaded")),wxPoint(110,105) ,wxSize(500,25)));
-    m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("loaded")),wxPoint(195,105) ,wxSize(500,25)));
-    m_widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxSize(170,20));
+    m_widget_button = widget_button;    // pointer to the button in parent dialog
+    m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){ toggle_advanced(true); });
+
+    m_number_of_extruders = (int)(sqrt(matrix.size())+0.001);
+
+    m_sizer_simple          = new wxBoxSizer(wxVERTICAL);
+    m_sizer_advanced        = new wxBoxSizer(wxVERTICAL);
+    auto gridsizer_simple   = new wxGridSizer(3,10,10);
+    auto gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1,10,10);
+
+
+    // First create controls for simple mode and assign them to m_sizer_simple:
+    m_sizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),wxPoint(40,25), wxSize(450,35)),-1,wxEXPAND,10);
+    m_sizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("Volume to purge (mm\u00B3) when the filament is being")),wxPoint(40,85) ,/*wxSize(500,25)*/wxDefaultSize,wxALIGN_LEFT),-1,wxEXPAND|wxALIGN_CENTER,10);
+
+    gridsizer_simple->Add(0,-1,wxALL,10);
+    gridsizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("unloaded")),wxPoint(110,105) ,/*wxSize(80,25)*/wxDefaultSize,wxALIGN_CENTER),-1,wxALIGN_CENTER,10);
+    gridsizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("loaded")),wxPoint(195,105) ,/*wxSize(80,25)*/wxDefaultSize,wxALIGN_CENTER),-1,wxALIGN_CENTER,10);
 
     for (unsigned int i=0;i<m_number_of_extruders;++i) {
-        m_notadvanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Tool #"))<<i+1<<": ",wxPoint(20,135+30*i) ,wxSize(150,25),wxALIGN_LEFT));
-        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,130+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i]));
-        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,130+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1]));
+        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,130+30*i),/*wxSize(50,25)*/wxDefaultSize,wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i]));
+        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,130+30*i),/*wxSize(50,25)*/wxDefaultSize,wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1]));
+        gridsizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("Tool #"))<<i+1<<": ",wxPoint(20,135+30*i) ,/*wxSize(75,25)*/wxDefaultSize,wxALIGN_LEFT),-1,wxALL,10);
+        gridsizer_simple->Add(m_old.back(),-1,wxALIGN_CENTER,10);
+        gridsizer_simple->Add(m_new.back(),-1,wxALIGN_CENTER,10);
     }
 
+    // Now the same for advanced mode:
     wxPoint origin(50,85);
-    m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),wxPoint(40,25) ,wxSize(500,35)));
+    m_sizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),wxPoint(40,25) ,/*wxSize(500,35)*/wxDefaultSize),-1,wxALL,10);
+    m_sizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString(L("Filament changed to")),origin+wxPoint(75,0) ,/*wxSize(500,25)*/wxDefaultSize),-1,wxALL,10);
+
     for (unsigned int i=0;i<m_number_of_extruders;++i) {
         edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
-        m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(45+60*i,25) ,wxSize(20,25)));
-        m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(0,50+30*i) ,wxSize(500,25)));
+
         for (unsigned int j=0;j<m_number_of_extruders;++j) {
-            edit_boxes.back().push_back(new wxTextCtrl(this,wxID_ANY,wxEmptyString,origin+wxPoint(25+60*i,45+30*j),wxSize(50,25)));
+            edit_boxes.back().push_back(new wxTextCtrl(this,wxID_ANY,wxEmptyString,origin+wxPoint(25+60*i,45+30*j),/*wxSize(50,25)*/wxDefaultSize));
             if (i==j)
                 edit_boxes[i][j]->Disable();
             else
                 edit_boxes[i][j]->SetValue(wxString("")<<int(matrix[m_number_of_extruders*j+i]));
         }
-        m_advanced_widgets.push_back(new wxStaticText(this,wxID_ANY,wxString(L("Filament changed to")),origin+wxPoint(75,0) ,wxSize(500,25)));
     }
 
-    m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){toggle_advanced(true);});
-    toggle_advanced();
+    gridsizer_advanced->Add(0,-1,wxALL,10);
+    for (unsigned int i=0;i<m_number_of_extruders;++i)
+        gridsizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(45+60*i,25) ,/*wxSize(20,25)*/wxDefaultSize),-1,wxALL,10);
+    for (unsigned int i=0;i<m_number_of_extruders;++i) {
+        gridsizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(0,50+30*i) ,/*wxSize(500,25)*/wxDefaultSize),-1,wxALL,10);
+        for (unsigned int j=0;j<m_number_of_extruders;++j)
+            gridsizer_advanced->Add(edit_boxes[j][i],-1,wxALL,10);
+    }
+
+
+    m_sizer_simple->Add(gridsizer_simple,-1,wxALL,10);
+    m_sizer_advanced->Add(gridsizer_advanced,-1,wxALL,10);
+    toggle_advanced(); // to show/hide what is appropriate
 }
 
 
 
 
-
+// Reads values from the (advanced) wiping matrix:
 std::vector<float> WipingPanel::read_matrix_values() {
     if (!m_advanced)
         fill_in_matrix();
@@ -183,7 +216,7 @@ std::vector<float> WipingPanel::read_matrix_values() {
     return output;
 }
 
-
+// Reads values from simple mode to save them for next time:
 std::vector<float> WipingPanel::read_extruders_values() {
     std::vector<float> output;
     for (unsigned int i=0;i<m_number_of_extruders;++i) {
@@ -193,7 +226,7 @@ std::vector<float> WipingPanel::read_extruders_values() {
     return output;
 }
 
-
+// This updates the "advanced" matrix based on values from "simple" mode
 void WipingPanel::fill_in_matrix() {
     for (unsigned i=0;i<m_number_of_extruders;++i) {
         for (unsigned j=0;j<m_number_of_extruders;++j) {
@@ -205,7 +238,7 @@ void WipingPanel::fill_in_matrix() {
 
 
 
-
+// Function to check if simple and advanced settings are matching
 bool WipingPanel::advanced_matches_simple() {
     for (unsigned i=0;i<m_number_of_extruders;++i) {
         for (unsigned j=0;j<m_number_of_extruders;++j) {
@@ -218,40 +251,27 @@ bool WipingPanel::advanced_matches_simple() {
 }
 
 
-
-void WipingPanel::toggle_advanced(bool user_button) {
-    if (m_advanced && !advanced_matches_simple() && user_button) {
+// Switches the dialog from simple to advanced mode and vice versa
+void WipingPanel::toggle_advanced(bool user_action) {
+    if (m_advanced && !advanced_matches_simple() && user_action) {
         if (wxMessageDialog(this,wxString(L("Switching to simple settings will discard changes done in the advanced mode!\n\nDo you want to proceed?")),
                             wxString(L("Warning")),wxYES_NO|wxICON_EXCLAMATION).ShowModal() != wxID_YES)
             return;
     }
-
-    m_advanced = !m_advanced;
-
-    if (!user_button) {                 // we were called from constructor
-        if (advanced_matches_simple())  // advanced and simple match - let's show simple version
-            m_advanced = false;
-        else
-            m_advanced = true;
+    if (user_action)
+        m_advanced = !m_advanced;                // user demands a change -> toggle
+    else {
+        m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
+        (m_advanced ? m_sizer_advanced : m_sizer_simple)->SetSizeHints(this);
+        SetSizer(m_advanced ? m_sizer_advanced : m_sizer_simple);
     }
 
-    for (unsigned i=0;i<m_number_of_extruders;++i) {        // shows/hides input controls
-            for (unsigned j=0;j<m_number_of_extruders;++j)
-                edit_boxes[i][j]->Show(m_advanced);
-            m_old[i]->Show(!m_advanced);
-            m_new[i]->Show(!m_advanced);
-    }
-    for (const auto& widget : m_advanced_widgets)          // shows/hides other widgets
-        widget->Show(m_advanced);
-    for (const auto& widget : m_notadvanced_widgets)
-        widget->Show(!m_advanced);
+   m_sizer_simple->Show(!m_advanced);
+   m_sizer_advanced->Show(m_advanced);
 
-    if (m_advanced) {
-        if (user_button) fill_in_matrix();  // otherwise keep values loaded from config
-        m_widget_button->SetLabel(L("Show simplified settings"));
-    }
-    else
-        m_widget_button->SetLabel(L("Show advanced settings"));
+    m_widget_button->SetLabel(m_advanced ? L("Show simplified settings") : L("Show advanced settings"));
+    if (m_advanced)
+        if (user_action) fill_in_matrix();  // otherwise keep values loaded from config
 
     this->Refresh();
 }
\ No newline at end of file
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index 54ae9596d..c8e7bd480 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -46,13 +46,13 @@ private:
 
 class WipingPanel : public wxPanel {
 public:
-    WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders);
+    WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button);
     std::vector<float> read_matrix_values();
     std::vector<float> read_extruders_values();
+    void toggle_advanced(bool user_action = false);
         
 private:
     void fill_in_matrix();
-    void toggle_advanced(bool user_button = false);
     bool advanced_matches_simple();
         
     std::vector<wxSpinCtrl*> m_old;
@@ -60,9 +60,11 @@ private:
     std::vector<wxWindow*>   m_advanced_widgets;
     std::vector<wxWindow*>   m_notadvanced_widgets;
     std::vector<std::vector<wxTextCtrl*>> edit_boxes;
-    wxButton* m_widget_button           = nullptr;
     unsigned int m_number_of_extruders  = 0;
     bool m_advanced                     = false;
+    wxBoxSizer*m_sizer_simple = nullptr;
+    wxBoxSizer*m_sizer_advanced = nullptr;
+    wxButton* m_widget_button     = nullptr;
 };
 
 

From 985b414c64ba2c86be36e60b3a3134127b6146bc Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 22 Mar 2018 13:37:01 +0100
Subject: [PATCH 051/118] Removed parameter 'wipe_tower_per_color_wipe' from UI
 and configuration layer

---
 lib/Slic3r/GUI/Plater.pm                    | 3 +--
 lib/Slic3r/GUI/Plater/3D.pm                 | 5 +++--
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 2 +-
 xs/src/libslic3r/Print.cpp                  | 3 +--
 xs/src/libslic3r/PrintConfig.cpp            | 9 ---------
 xs/src/slic3r/GUI/3DScene.cpp               | 8 +++++---
 xs/src/slic3r/GUI/Preset.cpp                | 2 +-
 xs/src/slic3r/GUI/Tab.cpp                   | 3 +--
 8 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index ad98fdf99..8a4953603 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -53,8 +53,7 @@ sub new {
         bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
         serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
         nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
-	wipe_tower_per_color_wipe wipe_tower_rotation_angle extruder_colour filament_colour
-        max_print_height
+	wipe_tower_rotation_angle extruder_colour filament_colour max_print_height
     )]);
     # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
     $self->{model} = Slic3r::Model->new;
diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index bd823c730..00035672f 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -205,8 +205,9 @@ sub reload_scene {
         if ($extruders_count > 1 && $self->{config}->single_extruder_multi_material && $self->{config}->wipe_tower &&
             ! $self->{config}->complete_objects) {
             $self->volumes->load_wipe_tower_preview(1000, 
-                $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, 
-                $self->{config}->wipe_tower_width, $self->{config}->wipe_tower_per_color_wipe * ($extruders_count - 1),
+                $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, $self->{config}->wipe_tower_width,
+		#$self->{config}->wipe_tower_per_color_wipe# 15 * ($extruders_count - 1), # this is just a hack when the config parameter became obsolete
+		15 * ($extruders_count - 1),
                 $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs);
         }
     }
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 734d61dc6..4f97f700f 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -42,7 +42,7 @@ public:
 	// y			-- y coordinates of wipe tower in mm ( left bottom corner )
 	// width		-- width of wipe tower in mm ( default 60 mm - leave as it is )
 	// wipe_area	-- space available for one toolchange in mm
-	WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, float cooling_tube_retraction,
+	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) :
 		m_wipe_tower_pos(x, y),
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 62c9c8819..cd66f6f56 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -199,7 +199,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
             || opt_key == "wipe_tower_x"
             || opt_key == "wipe_tower_y"
             || opt_key == "wipe_tower_width"
-            || opt_key == "wipe_tower_per_color_wipe"
             || opt_key == "wipe_tower_rotation_angle"
             || opt_key == "wipe_tower_bridging"
             || opt_key == "wiping_volumes_matrix"
@@ -1048,7 +1047,7 @@ void Print::_make_wipe_tower()
     // 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_per_color_wipe.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.wipe_tower_bridging), wiping_volumes, m_tool_ordering.first_extruder());
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index ff426c9a2..cb5bcea4a 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1818,15 +1818,6 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "wipe-tower-width=f";
     def->default_value = new ConfigOptionFloat(60.);
 
-    def = this->add("wipe_tower_per_color_wipe", coFloat);
-    def->label = "(Unused and will be likely removed)";//L("Per color change depth");
-    def->tooltip = L("Depth of a wipe color per color change. For N colors, there will be "
-                   "maximum (N-1) tool switches performed, therefore the total depth "
-                   "of the wipe tower will be (N-1) times this value.");
-    def->sidetext = L("mm");
-    def->cli = "wipe-tower-per-color-wipe=f";
-    def->default_value = new ConfigOptionFloat(15.);
-
     def = this->add("wipe_tower_rotation_angle", coFloat);
     def->label = L("Wipe tower rotation angle");
     def->tooltip = L("Wipe tower rotation angle with respect to x-axis ");
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 994c62a83..cbaa4ac90 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -2588,8 +2588,10 @@ void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, boo
     coordf_t max_z = print.objects[0]->model_object()->get_model()->bounding_box().max.z;
     const PrintConfig& config = print.config;
     unsigned int extruders_count = config.nozzle_diameter.size();
-    if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects)
-        volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, config.wipe_tower_per_color_wipe * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs);
+    if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
+        const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
+        volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs);
+    }
 }
 
-}
+} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 99d003f7a..a0d4fe5ff 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -199,7 +199,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_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_bridging",
+        "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging",
         "wiping_volumes_matrix", "wiping_volumes_extruders", "compatible_printers", "compatible_printers_condition"
 
     };
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 146696d1e..9716abfb4 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -517,7 +517,6 @@ void TabPrint::build()
 		optgroup->append_single_option_line("wipe_tower_x");
 		optgroup->append_single_option_line("wipe_tower_y");
 		optgroup->append_single_option_line("wipe_tower_width");
-		optgroup->append_single_option_line("wipe_tower_per_color_wipe");
 		optgroup->append_single_option_line("wipe_tower_rotation_angle");
         optgroup->append_single_option_line("wipe_tower_bridging");
         line = { _(L("Advanced")), "" };
@@ -863,7 +862,7 @@ void TabPrint::update()
 
 	bool have_wipe_tower = m_config->opt_bool("wipe_tower");
 	vec_enable.resize(0);
-	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "wipe_tower_bridging"};
+	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"};
 	for (auto el : vec_enable)
 		get_field(el)->toggle(have_wipe_tower);
     m_wipe_tower_btn->Enable(have_wipe_tower);

From 3fdd182f0c74366f4e164d65059b516b2616dec5 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 22 Mar 2018 16:13:41 +0100
Subject: [PATCH 052/118] Parameters describing cooling tubes position etc
 moved to separate page in Printer Settings

---
 xs/src/slic3r/GUI/Tab.cpp | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 9716abfb4..267e579b6 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -1099,7 +1099,7 @@ void TabPrinter::build()
 		optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){
 			size_t extruders_count = boost::any_cast<int>(optgroup->get_value("extruders_count"));
 			wxTheApp->CallAfter([this, opt_key, value, extruders_count](){
-				if (opt_key.compare("extruders_count")==0) {
+				if (opt_key.compare("extruders_count")==0 || opt_key.compare("single_extruder_multi_material")==0) {
 					extruders_count_changed(extruders_count);
 					update_dirty();
 				}
@@ -1110,6 +1110,7 @@ void TabPrinter::build()
 			});
 		};
 
+
 		if (!m_no_controller)
 		{
 		optgroup = page->new_optgroup(_(L("USB/Serial connection")));
@@ -1249,9 +1250,6 @@ void TabPrinter::build()
 		optgroup->append_single_option_line("use_firmware_retraction");
 		optgroup->append_single_option_line("use_volumetric_e");
 		optgroup->append_single_option_line("variable_layer_height");
-        optgroup->append_single_option_line("cooling_tube_retraction");
-		optgroup->append_single_option_line("cooling_tube_length");
-		optgroup->append_single_option_line("parking_pos_retraction");
 
 	page = add_options_page(_(L("Custom G-code")), "cog.png");
 		optgroup = page->new_optgroup(_(L("Start G-code")), 0);
@@ -1374,6 +1372,25 @@ void TabPrinter::build_extruder_pages(){
 	for (auto page_extruder : m_extruder_pages)
 		m_pages.push_back(page_extruder);
 	m_pages.push_back(page_note);
+
+    {
+        // if we have a single extruder MM setup, add a page with configuration options:
+        for (int i=0;i<m_pages.size();++i) // first make sure it's not there already
+            if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) {
+                m_pages.erase(m_pages.begin()+i);
+                break;
+            }
+        if ( m_extruder_pages.size()>1 && m_config->opt_bool("single_extruder_multi_material")) {
+            // create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
+            auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png",true);
+                auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
+                optgroup->append_single_option_line("cooling_tube_retraction");
+                optgroup->append_single_option_line("cooling_tube_length");
+                optgroup->append_single_option_line("parking_pos_retraction");
+            m_pages.insert(m_pages.begin()+1,page);
+        }
+    }
+
 	rebuild_page_tree();
 }
 

From 7d9e892edccea9ee7fa3c94937b258e6d744f2f6 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 27 Mar 2018 13:44:18 +0200
Subject: [PATCH 053/118] Added a button to open purging volumes dialog
 directly from Plater

---
 xs/src/slic3r/GUI/GUI.cpp | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 0410b7969..3c8504756 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -1,4 +1,5 @@
 #include "GUI.hpp"
+#include "WipeTowerDialog.hpp"
 
 #include <assert.h>
 
@@ -678,6 +679,32 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 	option = Option(def, "brim");
 	m_optgroup->append_single_option_line(option);
 
+
+    Line line = { _(L("")), "" };
+        line.widget = [config](wxWindow* parent){
+			auto wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+			auto sizer = new wxBoxSizer(wxHORIZONTAL);
+			sizer->Add(wiping_dialog_button);
+			wiping_dialog_button->Bind(wxEVT_BUTTON, ([config, parent](wxCommandEvent& e)
+			{
+                std::vector<double> init_matrix = (config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
+                std::vector<double> init_extruders = (config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
+
+                WipingDialog dlg(parent,std::vector<float>(init_matrix.begin(),init_matrix.end()),std::vector<float>(init_extruders.begin(),init_extruders.end()));
+
+				if (dlg.ShowModal() == wxID_OK) {
+                    std::vector<float> matrix = dlg.get_matrix();
+                    std::vector<float> extruders = dlg.get_extruders();
+                    (config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
+                    (config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
+                }
+			}));
+			return sizer;
+		};
+		m_optgroup->append_line(line);
+
+
+
 	sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxBottom, 1);
 }
 

From 56fef5302cd8cdf7e2b3c1f9190887ac909d27c1 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 27 Mar 2018 14:42:47 +0200
Subject: [PATCH 054/118] Added axis-labels to RammingChart

---
 xs/src/slic3r/GUI/RammingChart.cpp    | 23 ++++++++++++++++++-----
 xs/src/slic3r/GUI/RammingChart.hpp    |  2 +-
 xs/src/slic3r/GUI/WipeTowerDialog.cpp |  2 +-
 3 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
index e07b95839..79a701847 100644
--- a/xs/src/slic3r/GUI/RammingChart.cpp
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -2,6 +2,12 @@
 #include "RammingChart.hpp"
 
 
+//! macro used to mark string used at localization,
+//! return same string
+#define L(s) s
+
+
+
 wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
 
 
@@ -43,9 +49,9 @@ void Chart::draw(wxDC& dc) {
 
     // draw x-axis:
     float last_mark = -10000;
-    for (float math_x=int(visible_area.m_x*10)/10 ; math_x <= (visible_area.m_x+visible_area.m_width) ; math_x+=0.1) {
+    for (float math_x=int(visible_area.m_x*10)/10 ; math_x < (visible_area.m_x+visible_area.m_width) ; math_x+=0.1) {
         int x = math_to_screen(wxPoint2DDouble(math_x,visible_area.m_y)).x;
-        int y = m_rect.GetBottom(); 
+        int y = m_rect.GetBottom();
         if (x-last_mark < 50) continue;
         dc.DrawLine(x,y+3,x,y-3);
         dc.DrawText(wxString().Format(wxT("%.1f"), math_x),wxPoint(x-10,y+7));
@@ -54,7 +60,7 @@ void Chart::draw(wxDC& dc) {
     
     // draw y-axis:
     last_mark=10000;
-    for (int math_y=visible_area.m_y ; math_y <= (visible_area.m_y+visible_area.m_height) ; math_y+=1) {
+    for (int math_y=visible_area.m_y ; math_y < (visible_area.m_y+visible_area.m_height) ; math_y+=1) {
         int y = math_to_screen(wxPoint2DDouble(visible_area.m_x,math_y)).y;
         int x = m_rect.GetLeft();
         if (last_mark-y < 50) continue;    
@@ -63,8 +69,15 @@ void Chart::draw(wxDC& dc) {
         last_mark = y;
     }
     
-    
-    
+    // axis labels:
+    wxString label = L("Time (s)");
+    int text_width = 0;
+    int text_height = 0;
+    dc.GetTextExtent(label,&text_width,&text_height);
+    dc.DrawText(label,wxPoint(0.5*(m_rect.GetRight()+m_rect.GetLeft())-text_width/2.f, m_rect.GetBottom()+25));
+    label = L("Volumetric speed (mm\u00B3/s)");
+    dc.GetTextExtent(label,&text_width,&text_height);
+    dc.DrawRotatedText(label,wxPoint(0,0.5*(m_rect.GetBottom()+m_rect.GetTop())+text_width/2.f),90);
 }
 
 void Chart::mouse_right_button_clicked(wxMouseEvent& event) {
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
index 5ec8b33ee..aa1e7acc7 100644
--- a/xs/src/slic3r/GUI/RammingChart.hpp
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -16,7 +16,7 @@ public:
     Chart(wxWindow* parent, wxRect rect,const std::vector<std::pair<float,float>>& initial_buttons,int ramming_speed_size, float sampling) :
         wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize())
     {
-        m_rect=wxRect(wxPoint(30,0),rect.GetSize()-wxSize(30,30));
+        m_rect = wxRect(wxPoint(50,0),rect.GetSize()-wxSize(50,50));
         visible_area = wxRect2DDouble(0.0, 0.0, sampling*ramming_speed_size, 20.);
         m_buttons.clear();
         if (initial_buttons.size()>0)
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 1989a82f1..255c0d6bd 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -38,7 +38,7 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
 
 
 RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
-: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
+: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,400),wxBORDER_RAISED)
 {
     new wxStaticText(this,wxID_ANY,wxString("Total ramming time (s):"),     wxPoint(500,105),      wxSize(200,25),wxALIGN_LEFT);
     m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,       wxPoint(700,100),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0.,5.0,3.,0.5);        

From 4da6085ef883a8139417f25c6650045379c4cc44 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 28 Mar 2018 02:17:51 +0200
Subject: [PATCH 055/118] Corrected UI for WipingDialog

---
 xs/src/slic3r/GUI/Tab.cpp             |   2 +-
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 135 +++++++++++++++-----------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp |   4 +
 3 files changed, 83 insertions(+), 58 deletions(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 267e579b6..503ef54a4 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -521,7 +521,7 @@ void TabPrint::build()
         optgroup->append_single_option_line("wipe_tower_bridging");
         line = { _(L("Advanced")), "" };
         line.widget = [this](wxWindow* parent){
-			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _(L("Advanced settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _(L("Advanced settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
 			auto sizer = new wxBoxSizer(wxHORIZONTAL);
 			sizer->Add(m_wipe_tower_btn);
 			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 1989a82f1..8cd6528d9 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -11,7 +11,7 @@
 
 
 RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
-: wxDialog(parent, -1,  wxT("Ramming customization"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+: wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
     this->Centre();
     m_panel_ramming  = new RammingPanel(this,parameters);
@@ -20,7 +20,7 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
 
     auto main_sizer = new wxBoxSizer(wxVERTICAL);
     main_sizer->Add(m_panel_ramming, 1, wxEXPAND);
-    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
+    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM | wxTOP, 10);
     SetSizer(main_sizer);
     SetMinSize(GetSize());
     main_sizer->SetSizeHints(this);
@@ -102,24 +102,24 @@ std::string RammingPanel::get_parameters()
 }
 
 
-
+#define	ITEM_WIDTH	60
 // Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode:
 WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders)
-: wxDialog(parent, -1,  wxT(L("Wipe tower - Purging volume adjustment")), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+: wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
-    this->Centre();
-
     auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize);
     m_panel_wiping  = new WipingPanel(this,matrix,extruders, widget_button);
 
     auto main_sizer = new wxBoxSizer(wxVERTICAL);
-    main_sizer->Add(m_panel_wiping, 1, wxEXPAND);
 
+	// set min sizer width according to extruders count
+	const auto sizer_width = (int)((sqrt(matrix.size()) + 2.8)*ITEM_WIDTH);
+	main_sizer->SetMinSize(wxSize(sizer_width, -1));
 
-    main_sizer->Add(widget_button,0,wxALIGN_CENTER_HORIZONTAL|wxCENTER,10);
+    main_sizer->Add(m_panel_wiping, 0, wxEXPAND | wxALL, 5);
+	main_sizer->Add(widget_button, 0, wxALIGN_CENTER_HORIZONTAL | wxCENTER | wxBOTTOM, 5);
     main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
     SetSizer(main_sizer);
-    SetMinSize(GetSize());
     main_sizer->SetSizeHints(this);
 
     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
@@ -133,68 +133,91 @@ WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, co
     this->Show();
 }
 
-
+// This function allows to "play" with sizers parameters (like align or border)
+void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift/*=0*/)
+{
+	sizer->Add(new wxStaticText(page, wxID_ANY, info), 0, wxEXPAND | wxLEFT, 15);
+	auto table_sizer = new wxBoxSizer(wxVERTICAL);
+	sizer->Add(table_sizer, 0, wxALIGN_LEFT | wxLEFT, table_lshift);
+	table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50);
+	table_sizer->Add(grid_sizer, 0, wxALIGN_CENTER | wxTOP, 10);
+}
 
 // This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
 WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button)
-: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(500,350),wxBORDER_RAISED)
+: wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize,wxBORDER_RAISED)
 {
     m_widget_button = widget_button;    // pointer to the button in parent dialog
     m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){ toggle_advanced(true); });
 
     m_number_of_extruders = (int)(sqrt(matrix.size())+0.001);
 
+	// Create two switched panels with their own sizers
     m_sizer_simple          = new wxBoxSizer(wxVERTICAL);
     m_sizer_advanced        = new wxBoxSizer(wxVERTICAL);
-    auto gridsizer_simple   = new wxGridSizer(3,10,10);
-    auto gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1,10,10);
+	m_page_simple			= new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
+	m_page_advanced			= new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
+	m_page_simple->SetSizer(m_sizer_simple);
+	m_page_advanced->SetSizer(m_sizer_advanced);
 
+    auto gridsizer_simple   = new wxGridSizer(3, 5, 10);
+    auto gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1, 5, 1);
 
-    // First create controls for simple mode and assign them to m_sizer_simple:
-    m_sizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),wxPoint(40,25), wxSize(450,35)),-1,wxEXPAND,10);
-    m_sizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("Volume to purge (mm\u00B3) when the filament is being")),wxPoint(40,85) ,/*wxSize(500,25)*/wxDefaultSize,wxALIGN_LEFT),-1,wxEXPAND|wxALIGN_CENTER,10);
+	// First create controls for advanced mode and assign them to m_page_advanced:
+	for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
+		edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
 
-    gridsizer_simple->Add(0,-1,wxALL,10);
-    gridsizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("unloaded")),wxPoint(110,105) ,/*wxSize(80,25)*/wxDefaultSize,wxALIGN_CENTER),-1,wxALIGN_CENTER,10);
-    gridsizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("loaded")),wxPoint(195,105) ,/*wxSize(80,25)*/wxDefaultSize,wxALIGN_CENTER),-1,wxALIGN_CENTER,10);
+		for (unsigned int j = 0; j < m_number_of_extruders; ++j) {
+			edit_boxes.back().push_back(new wxTextCtrl(m_page_advanced, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(ITEM_WIDTH, -1)));
+			if (i == j)
+				edit_boxes[i][j]->Disable();
+			else
+				edit_boxes[i][j]->SetValue(wxString("") << int(matrix[m_number_of_extruders*j + i]));
+		}
+	}
+	gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("")));
+	for (unsigned int i = 0; i < m_number_of_extruders; ++i)
+		gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+	for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
+		gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+		for (unsigned int j = 0; j < m_number_of_extruders; ++j)
+			gridsizer_advanced->Add(edit_boxes[j][i], 0);
+	}
 
-    for (unsigned int i=0;i<m_number_of_extruders;++i) {
-        m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,130+30*i),/*wxSize(50,25)*/wxDefaultSize,wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i]));
-        m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,130+30*i),/*wxSize(50,25)*/wxDefaultSize,wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1]));
-        gridsizer_simple->Add(new wxStaticText(this,wxID_ANY,wxString(L("Tool #"))<<i+1<<": ",wxPoint(20,135+30*i) ,/*wxSize(75,25)*/wxDefaultSize,wxALIGN_LEFT),-1,wxALL,10);
-        gridsizer_simple->Add(m_old.back(),-1,wxALIGN_CENTER,10);
-        gridsizer_simple->Add(m_new.back(),-1,wxALIGN_CENTER,10);
-    }
+	// collect and format sizer
+	format_sizer(m_sizer_advanced, m_page_advanced, gridsizer_advanced,
+		_(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),
+		_(L("Filament changed to")));
 
-    // Now the same for advanced mode:
-    wxPoint origin(50,85);
-    m_sizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),wxPoint(40,25) ,/*wxSize(500,35)*/wxDefaultSize),-1,wxALL,10);
-    m_sizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString(L("Filament changed to")),origin+wxPoint(75,0) ,/*wxSize(500,25)*/wxDefaultSize),-1,wxALL,10);
+	// Hide preview page before new page creating 
+	// It allows to do that from a beginning of the main panel
+	m_page_advanced->Hide(); 
 
-    for (unsigned int i=0;i<m_number_of_extruders;++i) {
-        edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
+	// Now the same for simple mode:
+	gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString("")), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+	gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("unloaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+    gridsizer_simple->Add(new wxStaticText(m_page_simple,wxID_ANY,wxString(_(L("loaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
 
-        for (unsigned int j=0;j<m_number_of_extruders;++j) {
-            edit_boxes.back().push_back(new wxTextCtrl(this,wxID_ANY,wxEmptyString,origin+wxPoint(25+60*i,45+30*j),/*wxSize(50,25)*/wxDefaultSize));
-            if (i==j)
-                edit_boxes[i][j]->Disable();
-            else
-                edit_boxes[i][j]->SetValue(wxString("")<<int(matrix[m_number_of_extruders*j+i]));
-        }
-    }
+	for (unsigned int i=0;i<m_number_of_extruders;++i) {
+        m_old.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(80, -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i]));
+        m_new.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(80, -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1]));
+		gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+        gridsizer_simple->Add(m_old.back(),0);
+        gridsizer_simple->Add(m_new.back(),0);
+	}
 
-    gridsizer_advanced->Add(0,-1,wxALL,10);
-    for (unsigned int i=0;i<m_number_of_extruders;++i)
-        gridsizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(45+60*i,25) ,/*wxSize(20,25)*/wxDefaultSize),-1,wxALL,10);
-    for (unsigned int i=0;i<m_number_of_extruders;++i) {
-        gridsizer_advanced->Add(new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(0,50+30*i) ,/*wxSize(500,25)*/wxDefaultSize),-1,wxALL,10);
-        for (unsigned int j=0;j<m_number_of_extruders;++j)
-            gridsizer_advanced->Add(edit_boxes[j][i],-1,wxALL,10);
-    }
+	// collect and format sizer
+	format_sizer(m_sizer_simple, m_page_simple, gridsizer_simple,
+		_(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),
+		_(L("Volume to purge (mm\u00B3) when the filament is being")), 50);
 
+	m_sizer = new wxBoxSizer(wxVERTICAL);
+	m_sizer->Add(m_page_simple, 0, wxEXPAND | wxALL, 25);
+	m_sizer->Add(m_page_advanced, 0, wxEXPAND | wxALL, 25);
+
+	m_sizer->SetSizeHints(this);
+	SetSizer(m_sizer);
 
-    m_sizer_simple->Add(gridsizer_simple,-1,wxALL,10);
-    m_sizer_advanced->Add(gridsizer_advanced,-1,wxALL,10);
     toggle_advanced(); // to show/hide what is appropriate
 }
 
@@ -260,18 +283,16 @@ void WipingPanel::toggle_advanced(bool user_action) {
     }
     if (user_action)
         m_advanced = !m_advanced;                // user demands a change -> toggle
-    else {
+    else
         m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
-        (m_advanced ? m_sizer_advanced : m_sizer_simple)->SetSizeHints(this);
-        SetSizer(m_advanced ? m_sizer_advanced : m_sizer_simple);
-    }
 
-   m_sizer_simple->Show(!m_advanced);
-   m_sizer_advanced->Show(m_advanced);
+	(m_advanced ? m_page_advanced : m_page_simple)->Show();
+	(!m_advanced ? m_page_advanced : m_page_simple)->Hide();
 
     m_widget_button->SetLabel(m_advanced ? L("Show simplified settings") : L("Show advanced settings"));
     if (m_advanced)
         if (user_action) fill_in_matrix();  // otherwise keep values loaded from config
 
-    this->Refresh();
+   m_sizer->Layout();
+   Refresh();
 }
\ No newline at end of file
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index c8e7bd480..7448cd1e5 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -50,6 +50,7 @@ public:
     std::vector<float> read_matrix_values();
     std::vector<float> read_extruders_values();
     void toggle_advanced(bool user_action = false);
+	void format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift=0);
         
 private:
     void fill_in_matrix();
@@ -62,6 +63,9 @@ private:
     std::vector<std::vector<wxTextCtrl*>> edit_boxes;
     unsigned int m_number_of_extruders  = 0;
     bool m_advanced                     = false;
+	wxPanel*	m_page_simple = nullptr;
+	wxPanel*	m_page_advanced = nullptr;
+    wxBoxSizer*	m_sizer = nullptr;
     wxBoxSizer*m_sizer_simple = nullptr;
     wxBoxSizer*m_sizer_advanced = nullptr;
     wxButton* m_widget_button     = nullptr;

From 0d2637fede41b966361fbc03876a892e1d64ad56 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 28 Mar 2018 10:16:04 +0200
Subject: [PATCH 056/118] t tests changed so they don't fail on the extruder
 number check

---
 t/combineinfill.t | 2 +-
 t/custom_gcode.t  | 4 +++-
 t/fill.t          | 4 +++-
 t/multi.t         | 6 ++++++
 t/retraction.t    | 4 +++-
 5 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/t/combineinfill.t b/t/combineinfill.t
index e94cf9eb5..5402a84f5 100644
--- a/t/combineinfill.t
+++ b/t/combineinfill.t
@@ -57,7 +57,7 @@ plan tests => 8;
     my $config = Slic3r::Config::new_from_defaults;
     $config->set('layer_height', 0.2);
     $config->set('first_layer_height', 0.2);
-    $config->set('nozzle_diameter', [0.5]);
+    $config->set('nozzle_diameter', [0.5,0.5,0.5,0.5]);
     $config->set('infill_every_layers', 2);
     $config->set('perimeter_extruder', 1);
     $config->set('infill_extruder', 2);
diff --git a/t/custom_gcode.t b/t/custom_gcode.t
index bafcd4610..31d1d48e0 100644
--- a/t/custom_gcode.t
+++ b/t/custom_gcode.t
@@ -49,7 +49,6 @@ use Slic3r::Test;
     my $parser = Slic3r::GCode::PlaceholderParser->new;
     my $config = Slic3r::Config::new_from_defaults;
     $config->set('printer_notes', '  PRINTER_VENDOR_PRUSA3D  PRINTER_MODEL_MK2  ');
-    $config->set('nozzle_diameter', [0.6, 0.6, 0.6, 0.6]);
     $parser->apply_config($config);
     $parser->set('foo' => 0);
     $parser->set('bar' => 2);
@@ -123,6 +122,7 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config->new;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('extruder', 2);
     $config->set('first_layer_temperature', [200,205]);
     
@@ -204,6 +204,7 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config->new;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6,0.6]);
     $config->set('start_gcode', qq!
 ;substitution:{if infill_extruder==1}if block
          {elsif infill_extruder==2}elsif block 1
@@ -228,6 +229,7 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config->new;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('start_gcode', 
         ';substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}' .
         '{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}' .
diff --git a/t/fill.t b/t/fill.t
index a6fe8619c..dd9eee487 100644
--- a/t/fill.t
+++ b/t/fill.t
@@ -164,6 +164,7 @@ SKIP:
 
 for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
     my $config = Slic3r::Config::new_from_defaults;
+    $config->set('nozzle_diameter', [0.4,0.4,0.4,0.4]);
     $config->set('fill_pattern', $pattern);
     $config->set('external_fill_pattern', $pattern);
     $config->set('perimeters', 1);
@@ -195,6 +196,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
 
 {
     my $config = Slic3r::Config::new_from_defaults;
+    $config->set('nozzle_diameter', [0.4,0.4,0.4,0.4]);
     $config->set('infill_only_where_needed', 1);
     $config->set('bottom_solid_layers', 0);
     $config->set('infill_extruder', 2);
@@ -276,7 +278,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
     $config->set('fill_density', 0);
     $config->set('layer_height', 0.2);
     $config->set('first_layer_height', 0.2);
-    $config->set('nozzle_diameter', [0.35]);
+    $config->set('nozzle_diameter', [0.35,0.35,0.35,0.35]);
     $config->set('infill_extruder', 2);
     $config->set('solid_infill_extruder', 2);
     $config->set('infill_extrusion_width', 0.52);
diff --git a/t/multi.t b/t/multi.t
index 49d35d907..75ce0c286 100644
--- a/t/multi.t
+++ b/t/multi.t
@@ -16,6 +16,7 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config::new_from_defaults;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('raft_layers', 2);
     $config->set('infill_extruder', 2);
     $config->set('solid_infill_extruder', 3);
@@ -89,6 +90,7 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config::new_from_defaults;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('support_material_extruder', 3);
     
     my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
@@ -97,6 +99,7 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config->new;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('extruder', 2);
     
     my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
@@ -105,6 +108,7 @@ use Slic3r::Test;
 
 {
     my $config = Slic3r::Config->new;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('perimeter_extruder', 2);
     $config->set('infill_extruder', 2);
     $config->set('support_material_extruder', 2);
@@ -126,6 +130,7 @@ use Slic3r::Test;
     $upper_config->set('bottom_solid_layers', 1);
     $upper_config->set('top_solid_layers', 0);
     my $config = Slic3r::Config::new_from_defaults;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('fill_density', 0);
     $config->set('solid_infill_speed', 99);
     $config->set('top_solid_infill_speed', 99);
@@ -172,6 +177,7 @@ use Slic3r::Test;
     my $object = $model->objects->[0];
     
     my $config = Slic3r::Config::new_from_defaults;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('layer_height', 0.4);
     $config->set('first_layer_height', '100%');
     $config->set('skirts', 0);
diff --git a/t/retraction.t b/t/retraction.t
index d7f1ea145..6e6a130ca 100644
--- a/t/retraction.t
+++ b/t/retraction.t
@@ -95,6 +95,7 @@ use Slic3r::Test qw(_eq);
         1;
     };
 
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('first_layer_height',      $config->layer_height);
     $config->set('first_layer_speed',       '100%');
     $config->set('start_gcode',             '');  # to avoid dealing with the nozzle lift in start G-code
@@ -207,6 +208,7 @@ use Slic3r::Test qw(_eq);
 
 {
     my $config = Slic3r::Config::new_from_defaults;
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $config->set('start_gcode', '');
     $config->set('retract_lift', [3, 4]);
     
@@ -255,4 +257,4 @@ use Slic3r::Test qw(_eq);
         'Z is not lifted above the configured value for 2. extruder';
 }
 
-__END__
\ No newline at end of file
+__END__

From 7951349a1f64771848ca51147c7ac0474d83fb5b Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 28 Mar 2018 10:32:23 +0200
Subject: [PATCH 057/118] Another t-test change to avoid fails

---
 t/custom_gcode.t | 1 +
 1 file changed, 1 insertion(+)

diff --git a/t/custom_gcode.t b/t/custom_gcode.t
index 31d1d48e0..7c2a75f29 100644
--- a/t/custom_gcode.t
+++ b/t/custom_gcode.t
@@ -49,6 +49,7 @@ use Slic3r::Test;
     my $parser = Slic3r::GCode::PlaceholderParser->new;
     my $config = Slic3r::Config::new_from_defaults;
     $config->set('printer_notes', '  PRINTER_VENDOR_PRUSA3D  PRINTER_MODEL_MK2  ');
+    $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
     $parser->apply_config($config);
     $parser->set('foo' => 0);
     $parser->set('bar' => 2);

From 1b4dc685f4430a8ff1f35aea61dd920743b71c8b Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 28 Mar 2018 15:37:10 +0200
Subject: [PATCH 058/118] Connected filament_ramming_parameters to respective
 dialog, other minor changes

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 7c8181ff6..0c8f4fe9f 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -800,7 +800,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
 	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,(m_filpar[m_current_tool].cooling_time-14)/3);
+	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);
 
 	i = 0;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 4f97f700f..636b4b738 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -67,7 +67,7 @@ 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, int cooling_time, std::string ramming_parameters)
+                      float unloading_speed, float delay, float cooling_time, std::string ramming_parameters)
 	{
         //while (m_filpar.size() < idx+1)   // makes sure the required element is in the vector
         m_filpar.push_back(FilamentParameters());
@@ -205,7 +205,7 @@ private:
         float               loading_speed = 0.f;
         float               unloading_speed = 0.f;
         float               delay = 0.f ;
-        int                 cooling_time = 0;
+        float               cooling_time = 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/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index cb5bcea4a..aef9a8fa2 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -470,14 +470,14 @@ PrintConfigDef::PrintConfigDef()
     def->min = 0;
     def->default_value = new ConfigOptionFloats { 0. };
     
-    def = this->add("filament_cooling_time", coInts);
+    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->min = 0;
-    def->default_value = new ConfigOptionInts { 14 };
+    def->default_value = new ConfigOptionFloats { 14.f };
 
     def = this->add("filament_ramming_parameters", coStrings);
     def->label = L("Ramming parameters");
@@ -1779,7 +1779,7 @@ PrintConfigDef::PrintConfigDef()
     def->default_value = new ConfigOptionBool(false);
 
     def = this->add("wiping_volumes_extruders", coFloats);
-    def->label = L("Extrusion multiplier");
+    def->label = L("Purging volumes - load/unload volumes");
     def->tooltip = L("This vector saves required volumes to change from/to each tool used on the "
                      "wipe tower. These values are used to simplify creation of the full purging "
                      "volumes below. ");
@@ -1787,7 +1787,7 @@ PrintConfigDef::PrintConfigDef()
     def->default_value = new ConfigOptionFloats { 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f  };
 
     def = this->add("wiping_volumes_matrix", coFloats);
-    def->label = L("Extrusion multiplier");
+    def->label = L("Purging volumes - matrix");
     def->tooltip = L("This matrix describes volumes (in cubic milimetres) required to purge the"
                      " new filament on the wipe tower for any given pair of tools. ");
     def->cli = "wiping-volumes-matrix=f@";
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index d184a2161..be64d35bd 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -469,7 +469,7 @@ public:
     ConfigOptionFloats              filament_loading_speed;
     ConfigOptionFloats              filament_unloading_speed;
     ConfigOptionFloats              filament_toolchange_delay;
-    ConfigOptionInts                filament_cooling_time;
+    ConfigOptionFloats              filament_cooling_time;
     ConfigOptionStrings             filament_ramming_parameters;
     ConfigOptionBool                gcode_comments;
     ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 267e579b6..82d4c456d 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -521,7 +521,7 @@ void TabPrint::build()
         optgroup->append_single_option_line("wipe_tower_bridging");
         line = { _(L("Advanced")), "" };
         line.widget = [this](wxWindow* parent){
-			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _(L("Advanced settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _(L("Purging volumes"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
 			auto sizer = new wxBoxSizer(wxHORIZONTAL);
 			sizer->Add(m_wipe_tower_btn);
 			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
@@ -958,10 +958,8 @@ void TabFilament::build()
             ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
 			{
                 RammingDialog dlg(this,(m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0));
-                if (dlg.ShowModal() == wxID_OK) {
-                    wxMessageBox("DIALOG OK:\n"+dlg.get_parameters());
-                    //load_key_value("wipe_tower_advanced", dlg.get_parameters());
-                }
+                if (dlg.ShowModal() == wxID_OK)
+                    (m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0) = dlg.get_parameters();
 			}));
 			return sizer;
 		};

From c61ffe9f6b7a776f4bc468fbcaf0f7d794197b5e Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 28 Mar 2018 20:31:36 +0200
Subject: [PATCH 059/118] Corrected UI for RammingDialog

---
 xs/src/slic3r/GUI/RammingChart.cpp    |  2 +-
 xs/src/slic3r/GUI/Tab.cpp             |  2 +-
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 84 +++++++++++++++++----------
 3 files changed, 54 insertions(+), 34 deletions(-)

diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
index e07b95839..7b6730c1b 100644
--- a/xs/src/slic3r/GUI/RammingChart.cpp
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -59,7 +59,7 @@ void Chart::draw(wxDC& dc) {
         int x = m_rect.GetLeft();
         if (last_mark-y < 50) continue;    
         dc.DrawLine(x-3,y,x+3,y);
-        dc.DrawText(wxString()<<math_y,wxPoint(x-25,y-7));
+        dc.DrawText(wxString()<<math_y,wxPoint(x-25,y-2/*7*/));
         last_mark = y;
     }
     
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 503ef54a4..86fc5d671 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -951,7 +951,7 @@ void TabFilament::build()
         optgroup->append_single_option_line("filament_cooling_time");
         line = { _(L("Ramming")), "" };
         line.widget = [this](wxWindow* parent){
-			auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+			auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
             auto sizer = new wxBoxSizer(wxHORIZONTAL);
 			sizer->Add(ramming_dialog_btn);
             
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 8cd6528d9..b491415da 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -11,18 +11,16 @@
 
 
 RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
-: wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+: wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
-    this->Centre();
     m_panel_ramming  = new RammingPanel(this,parameters);
     m_panel_ramming->Show(true);
     this->Show();
 
     auto main_sizer = new wxBoxSizer(wxVERTICAL);
-    main_sizer->Add(m_panel_ramming, 1, wxEXPAND);
-    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM | wxTOP, 10);
+    main_sizer->Add(m_panel_ramming, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
+    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBOTTOM, 10);
     SetSizer(main_sizer);
-    SetMinSize(GetSize());
     main_sizer->SetSizeHints(this);
 
     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
@@ -38,34 +36,48 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
 
 
 RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
-: wxPanel(parent,wxID_ANY,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED)
+: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,/*wxPoint(50,50), wxSize(800,350),*/wxBORDER_RAISED)
 {
-    new wxStaticText(this,wxID_ANY,wxString("Total ramming time (s):"),     wxPoint(500,105),      wxSize(200,25),wxALIGN_LEFT);
-    m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,       wxPoint(700,100),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0.,5.0,3.,0.5);        
-    new wxStaticText(this,wxID_ANY,wxString("Total rammed volume (mm\u00B3):"),  wxPoint(500,135),      wxSize(200,25),wxALIGN_LEFT);
-    m_widget_volume = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,           wxPoint(700,130),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,10000,0);        
-    new wxStaticText(this,wxID_ANY,wxString("Ramming line width (%):"),     wxPoint(500,205),      wxSize(200,25),wxALIGN_LEFT);
-    m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,       wxPoint(700,200),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        
-    new wxStaticText(this,wxID_ANY,wxString("Ramming line spacing (%):"),   wxPoint(500,235),      wxSize(200,25),wxALIGN_LEFT);
-    m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,     wxPoint(700,230),      wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);        
-    
-    std::stringstream stream{parameters};
-    stream >> m_ramming_line_width_multiplicator >> m_ramming_step_multiplicator;
-    int ramming_speed_size = 0;
-    float dummy = 0.f;
-    while (stream >> dummy)
-        ++ramming_speed_size;
-    stream.clear();
-    stream.get();    
-    
-    std::vector<std::pair<float,float>> buttons;
-    float x = 0.f;
-    float y = 0.f;
-    while (stream >> x >> y)
-        buttons.push_back(std::make_pair(x,y));        
-    
-    m_chart = new Chart(this,wxRect(10,10,480,360),buttons,ramming_speed_size,0.25f);
-    
+	auto sizer_chart = new wxBoxSizer(wxVERTICAL);
+	auto sizer_param = new wxBoxSizer(wxVERTICAL);
+
+	std::stringstream stream{ parameters };
+	stream >> m_ramming_line_width_multiplicator >> m_ramming_step_multiplicator;
+	int ramming_speed_size = 0;
+	float dummy = 0.f;
+	while (stream >> dummy)
+		++ramming_speed_size;
+	stream.clear();
+	stream.get();
+
+	std::vector<std::pair<float, float>> buttons;
+	float x = 0.f;
+	float y = 0.f;
+	while (stream >> x >> y)
+		buttons.push_back(std::make_pair(x, y));
+
+	m_chart = new Chart(this, wxRect(10, 10, 480, 360), buttons, ramming_speed_size, 0.25f);
+ 	sizer_chart->Add(m_chart, 0, wxALL, 5);
+
+    m_widget_time						= new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,0.,5.0,3.,0.5);        
+    m_widget_volume							  = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,0,10000,0);        
+    m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100);        
+    m_widget_ramming_step_multiplicator		  = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100);        
+
+	auto gsizer_param = new wxFlexGridSizer(2, 5, 15);
+	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total ramming time (s):")))), 0, wxALIGN_CENTER_VERTICAL);
+	gsizer_param->Add(m_widget_time);
+	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume (mm"))+"\u00B3):")), 0, wxALIGN_CENTER_VERTICAL);
+	gsizer_param->Add(m_widget_volume);
+	gsizer_param->AddSpacer(20);
+	gsizer_param->AddSpacer(20);
+	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line width (%):")))), 0, wxALIGN_CENTER_VERTICAL);
+	gsizer_param->Add(m_widget_ramming_line_width_multiplicator);
+	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line spacing (%):")))), 0, wxALIGN_CENTER_VERTICAL);
+	gsizer_param->Add(m_widget_ramming_step_multiplicator);
+
+	sizer_param->Add(gsizer_param, 0, wxTOP, 100);
+
     m_widget_time->SetValue(m_chart->get_time());
     m_widget_time->SetDigits(2);
     m_widget_volume->SetValue(m_chart->get_volume());
@@ -75,6 +87,14 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
     
     m_widget_ramming_step_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
     m_widget_ramming_line_width_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
+
+	auto sizer = new wxBoxSizer(wxHORIZONTAL);
+	sizer->Add(sizer_chart, 0, wxALL, 5);
+	sizer->Add(sizer_param, 0, wxALL, 10);
+
+	sizer->SetSizeHints(this);
+	SetSizer(sizer);
+
     m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(m_widget_time->GetValue(),-1);});
     m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){});      // do nothing - prevents the user to change the value
     m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){});    // do nothing - prevents the user to change the value   

From 572008546d3a6758af20963dcde20846add08445 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 29 Mar 2018 12:04:09 +0200
Subject: [PATCH 060/118] Vertical label in wiping matrix dialog

---
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 24 +++++++++++++++++-------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp |  2 --
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index b69d7c6f0..05be20cd6 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -11,7 +11,7 @@
 
 
 RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
-: wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+: wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
 {
     m_panel_ramming  = new RammingPanel(this,parameters);
     m_panel_ramming->Show(true);
@@ -36,7 +36,7 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
 
 
 RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
-: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,/*wxPoint(50,50), wxSize(800,350),*/wxBORDER_RAISED).
+: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED*/)
 {
 	auto sizer_chart = new wxBoxSizer(wxVERTICAL);
 	auto sizer_param = new wxBoxSizer(wxVERTICAL);
@@ -125,7 +125,7 @@ std::string RammingPanel::get_parameters()
 #define	ITEM_WIDTH	60
 // Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode:
 WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders)
-: wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+: wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
 {
     auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize);
     m_panel_wiping  = new WipingPanel(this,matrix,extruders, widget_button);
@@ -156,16 +156,16 @@ WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, co
 // This function allows to "play" with sizers parameters (like align or border)
 void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift/*=0*/)
 {
-	sizer->Add(new wxStaticText(page, wxID_ANY, info), 0, wxEXPAND | wxLEFT, 15);
+	sizer->Add(new wxStaticText(page, wxID_ANY, info,wxDefaultPosition,wxSize(0,50)), 0, wxEXPAND | wxLEFT, 15);
 	auto table_sizer = new wxBoxSizer(wxVERTICAL);
-	sizer->Add(table_sizer, 0, wxALIGN_LEFT | wxLEFT, table_lshift);
+	sizer->Add(table_sizer, 0, wxALIGN_CENTER | wxCENTER, table_lshift);
 	table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50);
 	table_sizer->Add(grid_sizer, 0, wxALIGN_CENTER | wxTOP, 10);
 }
 
 // This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
 WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button)
-: wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize,wxBORDER_RAISED)
+: wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxBORDER_RAISED*/)
 {
     m_widget_button = widget_button;    // pointer to the button in parent dialog
     m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){ toggle_advanced(true); });
@@ -207,7 +207,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
 	// collect and format sizer
 	format_sizer(m_sizer_advanced, m_page_advanced, gridsizer_advanced,
 		_(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),
-		_(L("Filament changed to")));
+		_(L("Extruder changed to")));
 
 	// Hide preview page before new page creating 
 	// It allows to do that from a beginning of the main panel
@@ -239,6 +239,16 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
 	SetSizer(m_sizer);
 
     toggle_advanced(); // to show/hide what is appropriate
+    
+    m_page_advanced->Bind(wxEVT_PAINT,[this](wxPaintEvent&) {
+                                              wxPaintDC dc(m_page_advanced);
+                                              int y_pos = 0.5 * (edit_boxes[0][0]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetSize().y);
+                                              wxString label = L("From");
+                                              int text_width = 0;
+                                              int text_height = 0;
+                                              dc.GetTextExtent(label,&text_width,&text_height);
+                                              dc.DrawRotatedText(label,5,y_pos + text_width/2.f,90);
+    });
 }
 
 
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index 7448cd1e5..beb5c5f46 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -58,8 +58,6 @@ private:
         
     std::vector<wxSpinCtrl*> m_old;
     std::vector<wxSpinCtrl*> m_new;
-    std::vector<wxWindow*>   m_advanced_widgets;
-    std::vector<wxWindow*>   m_notadvanced_widgets;
     std::vector<std::vector<wxTextCtrl*>> edit_boxes;
     unsigned int m_number_of_extruders  = 0;
     bool m_advanced                     = false;

From 6fd3f38717affdd552b3b218ea69768e955a7d55 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 29 Mar 2018 14:23:45 +0200
Subject: [PATCH 061/118] Warning to show before ramming adjustements, vertical
 text in wiping dialog positioned better

---
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 21 ++++++++++++++-------
 xs/src/slic3r/GUI/WipeTowerDialog.hpp |  5 +++--
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 05be20cd6..df0552299 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -29,6 +29,12 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
         m_output_data = m_panel_ramming->get_parameters();
         EndModal(wxID_OK);
         },wxID_OK);
+    this->Show();
+    wxMessageDialog(this,L("Ramming denotes the rapid extrusion just before a tool change in a single-extruder MM printer. Its purpose is to "
+                   "properly shape the end of the unloaded filament so it does not prevent insertion of the new filament and can itself "
+                   "be reinserted later. This phase is important and different materials can require different extrusion speeds to get "
+                   "the good shape. For this reason, the extrusion rates during ramming are adjustable.\n\nThis is an expert-level "
+                   "setting, incorrect adjustment will likely lead to jams, extruder wheel grinding into filament etc."),L("Warning"),wxOK|wxICON_EXCLAMATION).ShowModal();
 }
 
 
@@ -181,7 +187,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
 	m_page_advanced->SetSizer(m_sizer_advanced);
 
     auto gridsizer_simple   = new wxGridSizer(3, 5, 10);
-    auto gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1, 5, 1);
+    m_gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1, 5, 1);
 
 	// First create controls for advanced mode and assign them to m_page_advanced:
 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
@@ -195,17 +201,17 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
 				edit_boxes[i][j]->SetValue(wxString("") << int(matrix[m_number_of_extruders*j + i]));
 		}
 	}
-	gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("")));
+	m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("")));
 	for (unsigned int i = 0; i < m_number_of_extruders; ++i)
-		gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+		m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
-		gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+		m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
 		for (unsigned int j = 0; j < m_number_of_extruders; ++j)
-			gridsizer_advanced->Add(edit_boxes[j][i], 0);
+			m_gridsizer_advanced->Add(edit_boxes[j][i], 0);
 	}
 
 	// collect and format sizer
-	format_sizer(m_sizer_advanced, m_page_advanced, gridsizer_advanced,
+	format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced,
 		_(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),
 		_(L("Extruder changed to")));
 
@@ -247,7 +253,8 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
                                               int text_width = 0;
                                               int text_height = 0;
                                               dc.GetTextExtent(label,&text_width,&text_height);
-                                              dc.DrawRotatedText(label,5,y_pos + text_width/2.f,90);
+                                              int xpos = m_gridsizer_advanced->GetPosition().x;
+                                              dc.DrawRotatedText(label,xpos-text_height,y_pos + text_width/2.f,90);
     });
 }
 
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
index beb5c5f46..d858062da 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -64,8 +64,9 @@ private:
 	wxPanel*	m_page_simple = nullptr;
 	wxPanel*	m_page_advanced = nullptr;
     wxBoxSizer*	m_sizer = nullptr;
-    wxBoxSizer*m_sizer_simple = nullptr;
-    wxBoxSizer*m_sizer_advanced = nullptr;
+    wxBoxSizer* m_sizer_simple = nullptr;
+    wxBoxSizer* m_sizer_advanced = nullptr;
+    wxGridSizer* m_gridsizer_advanced = nullptr;
     wxButton* m_widget_button     = nullptr;
 };
 

From 281732ca388909d475abd1e504add7ce06dcc196 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 29 Mar 2018 15:32:09 +0200
Subject: [PATCH 062/118] Variable layer height enabled, nozzle diameters
 properly passed to the wipe tower generator

---
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp |  2 +-
 xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 13 ++++++++-----
 xs/src/libslic3r/Print.cpp                  | 13 ++++++++++---
 3 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 0c8f4fe9f..889eaf2f5 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -1104,7 +1104,7 @@ void WipeTowerPrusaMM::plan_tower()
 		if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
 			m_wipe_tower_depth = this_layer_depth + m_perimeter_width;
 
-		for (int i = layer_index - 1; i >= 0 /*&& m_plan[i].depth < this_layer_depth*/; i--)
+		for (int i = layer_index - 1; i >= 0 ; i--)
 		{
 			if (m_plan[i].depth - this_layer_depth < 2*m_perimeter_width )
 				m_plan[i].depth = this_layer_depth;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 636b4b738..26c40fbc9 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -67,7 +67,7 @@ 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 unloading_speed, float delay, float cooling_time, 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,6 +79,9 @@ public:
         m_filpar[idx].unloading_speed = unloading_speed;
         m_filpar[idx].delay = delay;
         m_filpar[idx].cooling_time = cooling_time;
+        m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
+
+        m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
 
         std::stringstream stream{ramming_parameters};
         float speed = 0.f;
@@ -171,8 +174,7 @@ private:
 
 
     const bool  m_peters_wipe_tower   = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
-    const float Filament_Area         = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^3
-    const float Nozzle_Diameter       = 0.4f;  // nozzle diameter in mm
+    const float Filament_Area         = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^2
     const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
     const float WT_EPSILON            = 1e-3f;
 
@@ -194,7 +196,7 @@ private:
     float           m_bridging                  = 0.f;
     bool            m_adhesion                  = true;
 
-	float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
+	float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
 	float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
 
 
@@ -209,6 +211,7 @@ private:
         float               ramming_line_width_multiplicator = 0.f;
         float               ramming_step_multiplicator = 0.f;
         std::vector<float>  ramming_speed;
+        float               nozzle_diameter;
     };
 
 	// Extruder specific parameters.
@@ -235,7 +238,7 @@ private:
 	{
 		if ( layer_height < 0 )
 			return m_extrusion_flow;
-		return layer_height * ( Width_To_Nozzle_Ratio * Nozzle_Diameter - layer_height * (1-M_PI/4.f)) / (Filament_Area);
+		return layer_height * ( m_perimeter_width - layer_height * (1-M_PI/4.f)) / Filament_Area;
 	}
 
 	// Calculates length of extrusion line to extrude given volume
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index cd66f6f56..168bf1302 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -581,6 +581,12 @@ std::string Print::validate() const
             return "The Spiral Vase option can only be used when printing single material objects.";
     }
 
+    if (this->config.single_extruder_multi_material) {
+        for (size_t i=1; i<this->config.nozzle_diameter.values.size(); ++i)
+            if (this->config.nozzle_diameter.values[i] != this->config.nozzle_diameter.values[i-1])
+                return "All extruders must have the same diameter for single extruder multimaterial printer.";
+    }
+
     if (this->has_wipe_tower() && ! this->objects.empty()) {
         #if 0
         for (auto dmr : this->config.nozzle_diameter.values)
@@ -606,10 +612,10 @@ std::string Print::validate() const
             bool was_layer_height_profile_valid = object->layer_height_profile_valid;
             object->update_layer_height_profile();
             object->layer_height_profile_valid = was_layer_height_profile_valid;
-            for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
+            /*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
                 if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON &&
                     std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON)
-                    return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";
+                    return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";*/
         }
     }
     
@@ -1066,7 +1072,8 @@ void Print::_make_wipe_tower()
             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_ramming_parameters.get_at(i));
+            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.

From 89686b808d67f8d575ccd4b2f42ccc11e9846b51 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 3 Apr 2018 13:51:12 +0200
Subject: [PATCH 063/118] Added a check that all objects have the same layer
 height profile

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

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 168bf1302..267062125 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -612,6 +612,15 @@ std::string Print::validate() const
             bool was_layer_height_profile_valid = object->layer_height_profile_valid;
             object->update_layer_height_profile();
             object->layer_height_profile_valid = was_layer_height_profile_valid;
+
+            PrintObject* first_object = this->objects.front();
+            int i = 0;
+            while ( i < first_object->layer_height_profile.size() && i < object->layer_height_profile.size() ) {
+                if (std::abs(first_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;
+            }
+
             /*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
                 if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON &&
                     std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON)

From e5f23bc11d712f8c012197b0c5c1b50bf65d9077 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 3 Apr 2018 14:35:54 +0200
Subject: [PATCH 064/118] Fixed bug - negative feedrate was issued during
 loading of a new filament

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 889eaf2f5..4dbc7440e 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -786,7 +786,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
     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)/edist * m_filpar[m_current_tool].unloading_speed )
+          .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(oldx         ,-12    , 60.f * std::hypot(xdist,12)/12       * m_filpar[m_current_tool].unloading_speed*0.35f )
           .resume_preview();

From d54425a901a8f04d9253ea630219f537765ad4ac Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 3 Apr 2018 22:07:59 +0200
Subject: [PATCH 065/118] Added show/hide of "Purging volumes" button depending
 on a state of "wipe_tower"

---
 xs/src/slic3r/GUI/GUI.cpp             | 11 ++++++++---
 xs/src/slic3r/GUI/GUI.hpp             |  2 ++
 xs/src/slic3r/GUI/OptionsGroup.cpp    |  2 +-
 xs/src/slic3r/GUI/Tab.cpp             |  6 +++++-
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 12 ++++++------
 5 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 3c8504756..933a1ce0e 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -182,6 +182,7 @@ wxLocale*	g_wxLocale;
 
 std::shared_ptr<ConfigOptionsGroup>	m_optgroup;
 double m_brim_width = 0.0;
+wxButton*	g_wiping_dialog_button = nullptr;
 
 void set_wxapp(wxApp *app)
 {
@@ -682,10 +683,10 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 
     Line line = { _(L("")), "" };
         line.widget = [config](wxWindow* parent){
-			auto wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+			g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + "\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
 			auto sizer = new wxBoxSizer(wxHORIZONTAL);
-			sizer->Add(wiping_dialog_button);
-			wiping_dialog_button->Bind(wxEVT_BUTTON, ([config, parent](wxCommandEvent& e)
+			sizer->Add(g_wiping_dialog_button);
+			g_wiping_dialog_button->Bind(wxEVT_BUTTON, ([config, parent](wxCommandEvent& e)
 			{
                 std::vector<double> init_matrix = (config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
                 std::vector<double> init_extruders = (config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
@@ -713,4 +714,8 @@ ConfigOptionsGroup* get_optgroup()
 	return m_optgroup.get();
 }
 
+wxButton* get_wiping_dialog_button(){
+	return g_wiping_dialog_button;
+}
+
 } }
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 084b6de46..7561d256d 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -18,6 +18,7 @@ class wxArrayLong;
 class wxColour;
 class wxBoxSizer;
 class wxFlexGridSizer;
+class wxButton;
 
 namespace Slic3r { 
 
@@ -129,6 +130,7 @@ wxString	from_u8(const std::string &str);
 void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
 
 ConfigOptionsGroup* get_optgroup();
+wxButton*			get_wiping_dialog_button();
 
 }
 }
diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index 0be24824c..0f165e2d3 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -137,7 +137,7 @@ void OptionsGroup::append_line(const Line& line) {
     // If there's a widget, build it and add the result to the sizer.
 	if (line.widget != nullptr) {
 		auto wgt = line.widget(parent());
-		grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5);
+		grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 1);
 		return;
 	}
 	
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 50de384de..1d92439f9 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -335,7 +335,11 @@ void Tab::on_value_change(std::string opt_key, boost::any value)
 		bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
 		get_optgroup()->set_value("brim", val);
 	}
-
+	if (opt_key == "wipe_tower"){
+		m_config->opt_bool("wipe_tower") ? 
+			get_wiping_dialog_button()->Show() : 
+			get_wiping_dialog_button()->Hide();
+	}
 		
 	update();
 }
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index df0552299..421ae272d 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -30,11 +30,11 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
         EndModal(wxID_OK);
         },wxID_OK);
     this->Show();
-    wxMessageDialog(this,L("Ramming denotes the rapid extrusion just before a tool change in a single-extruder MM printer. Its purpose is to "
+    wxMessageDialog(this,_(L("Ramming denotes the rapid extrusion just before a tool change in a single-extruder MM printer. Its purpose is to "
                    "properly shape the end of the unloaded filament so it does not prevent insertion of the new filament and can itself "
                    "be reinserted later. This phase is important and different materials can require different extrusion speeds to get "
                    "the good shape. For this reason, the extrusion rates during ramming are adjustable.\n\nThis is an expert-level "
-                   "setting, incorrect adjustment will likely lead to jams, extruder wheel grinding into filament etc."),L("Warning"),wxOK|wxICON_EXCLAMATION).ShowModal();
+                   "setting, incorrect adjustment will likely lead to jams, extruder wheel grinding into filament etc.")),_(L("Warning")),wxOK|wxICON_EXCLAMATION).ShowModal();
 }
 
 
@@ -249,7 +249,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
     m_page_advanced->Bind(wxEVT_PAINT,[this](wxPaintEvent&) {
                                               wxPaintDC dc(m_page_advanced);
                                               int y_pos = 0.5 * (edit_boxes[0][0]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetSize().y);
-                                              wxString label = L("From");
+                                              wxString label = _(L("From"));
                                               int text_width = 0;
                                               int text_height = 0;
                                               dc.GetTextExtent(label,&text_width,&text_height);
@@ -314,8 +314,8 @@ bool WipingPanel::advanced_matches_simple() {
 // Switches the dialog from simple to advanced mode and vice versa
 void WipingPanel::toggle_advanced(bool user_action) {
     if (m_advanced && !advanced_matches_simple() && user_action) {
-        if (wxMessageDialog(this,wxString(L("Switching to simple settings will discard changes done in the advanced mode!\n\nDo you want to proceed?")),
-                            wxString(L("Warning")),wxYES_NO|wxICON_EXCLAMATION).ShowModal() != wxID_YES)
+        if (wxMessageDialog(this,wxString(_(L("Switching to simple settings will discard changes done in the advanced mode!\n\nDo you want to proceed?"))),
+                            wxString(_(L("Warning"))),wxYES_NO|wxICON_EXCLAMATION).ShowModal() != wxID_YES)
             return;
     }
     if (user_action)
@@ -326,7 +326,7 @@ void WipingPanel::toggle_advanced(bool user_action) {
 	(m_advanced ? m_page_advanced : m_page_simple)->Show();
 	(!m_advanced ? m_page_advanced : m_page_simple)->Hide();
 
-    m_widget_button->SetLabel(m_advanced ? L("Show simplified settings") : L("Show advanced settings"));
+    m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings")));
     if (m_advanced)
         if (user_action) fill_in_matrix();  // otherwise keep values loaded from config
 

From 42baeee0ed4f4346ab7dc1268614c9452e7325ff Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 4 Apr 2018 09:44:05 +0200
Subject: [PATCH 066/118] Fixed a bug regarding the sparse infill

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 4dbc7440e..e9523b731 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -1174,7 +1174,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
 
 		if (! layer_finished()) {
             auto finish_layer_toolchange = finish_layer();
-            if (layer_result.size() > 1) { // we will merge it to the last toolchange
+            if ( ! layer.tool_changes.empty() ) { // we will merge it to the last toolchange
                 auto& last_toolchange = layer_result.back();
                 if (last_toolchange.end_pos != finish_layer_toolchange.start_pos) {
                     char buf[2048];     // Add a travel move from tc1.end_pos to tc2.start_pos.

From 719adfbaf3c4584da520f5bc47c302b85c301c70 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 4 Apr 2018 09:59:41 +0200
Subject: [PATCH 067/118] The layer height check for multiple objects is now
 only enabled when variable layer height is enabled

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

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 267062125..3ce73ab9a 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -613,12 +613,14 @@ std::string Print::validate() const
             object->update_layer_height_profile();
             object->layer_height_profile_valid = was_layer_height_profile_valid;
 
-            PrintObject* first_object = this->objects.front();
-            int i = 0;
-            while ( i < first_object->layer_height_profile.size() && i < object->layer_height_profile.size() ) {
-                if (std::abs(first_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 ( this->config.variable_layer_height ) {
+                PrintObject* first_object = this->objects.front();
+                int i = 0;
+                while ( i < first_object->layer_height_profile.size() && i < object->layer_height_profile.size() ) {
+                    if (std::abs(first_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;
+                }
             }
 
             /*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)

From be8acc5ac41e9472d45224bce19b36d9c36affb3 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 4 Apr 2018 10:18:26 +0200
Subject: [PATCH 068/118] Moved "wiping_volumes_extruders",
 "wiping_volumes_matrix" from print settings to project settings.

---
 xs/src/slic3r/GUI/GUI.cpp          | 11 ++--
 xs/src/slic3r/GUI/Preset.cpp       |  2 +-
 xs/src/slic3r/GUI/PresetBundle.cpp | 10 ++--
 xs/src/slic3r/GUI/Tab.cpp          | 89 +++++++-----------------------
 xs/src/slic3r/GUI/Tab.hpp          |  1 -
 5 files changed, 34 insertions(+), 79 deletions(-)

diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 933a1ce0e..ce47d527b 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -686,18 +686,19 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 			g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + "\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
 			auto sizer = new wxBoxSizer(wxHORIZONTAL);
 			sizer->Add(g_wiping_dialog_button);
-			g_wiping_dialog_button->Bind(wxEVT_BUTTON, ([config, parent](wxCommandEvent& e)
+			g_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e)
 			{
-                std::vector<double> init_matrix = (config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
-                std::vector<double> init_extruders = (config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
+				auto &config = g_PresetBundle->project_config;
+                std::vector<double> init_matrix = (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
+                std::vector<double> init_extruders = (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
 
                 WipingDialog dlg(parent,std::vector<float>(init_matrix.begin(),init_matrix.end()),std::vector<float>(init_extruders.begin(),init_extruders.end()));
 
 				if (dlg.ShowModal() == wxID_OK) {
                     std::vector<float> matrix = dlg.get_matrix();
                     std::vector<float> extruders = dlg.get_extruders();
-                    (config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
-                    (config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
+                    (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
+                    (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
                 }
 			}));
 			return sizer;
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index a0d4fe5ff..c52df1e35 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -200,7 +200,7 @@ const std::vector<std::string>& Preset::print_options()
         "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",
-        "wiping_volumes_matrix", "wiping_volumes_extruders", "compatible_printers", "compatible_printers_condition"
+        "compatible_printers", "compatible_printers_condition"
 
     };
     return s_opts;
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 1d4795832..0026e5ccf 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -32,7 +32,9 @@
 
 namespace Slic3r {
 
-static std::vector<std::string> s_project_options {   
+static std::vector<std::string> s_project_options {
+    "wiping_volumes_extruders",
+    "wiping_volumes_matrix"
 };
 
 PresetBundle::PresetBundle() :
@@ -722,11 +724,11 @@ void PresetBundle::update_multi_material_filament_presets()
 
 
     // Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator):
-    std::vector<double> old_matrix = (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
+    std::vector<double> old_matrix = this->project_config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values;
     size_t old_number_of_extruders = int(sqrt(old_matrix.size())+EPSILON);
     if (num_extruders != old_number_of_extruders) {
             // First verify if purging volumes presets for each extruder matches number of extruders
-            std::vector<double>& extruders = (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
+            std::vector<double>& extruders = this->project_config.option<ConfigOptionFloats>("wiping_volumes_extruders")->values;
             while (extruders.size() < 2*num_extruders) {
                 extruders.push_back(extruders.size()>1 ? extruders[0] : 50.);  // copy the values from the first extruder
                 extruders.push_back(extruders.size()>1 ? extruders[1] : 50.);
@@ -745,7 +747,7 @@ void PresetBundle::update_multi_material_filament_presets()
                 else
                     new_matrix.push_back( i==j ? 0. : extruders[2*i]+extruders[2*j+1]); // so it matches new extruder volumes
             }
-        (prints.get_edited_preset().config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = new_matrix;
+		this->project_config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values = new_matrix;
     }
 }
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 1d92439f9..32a56d9b5 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -523,28 +523,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");
-        line = { _(L("Advanced")), "" };
-        line.widget = [this](wxWindow* parent){
-			m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _(L("Purging volumes"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
-			auto sizer = new wxBoxSizer(wxHORIZONTAL);
-			sizer->Add(m_wipe_tower_btn);
-			m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
-			{
-                std::vector<double> init_matrix = (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
-                std::vector<double> init_extruders = (m_config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
-
-                WipingDialog dlg(this,std::vector<float>(init_matrix.begin(),init_matrix.end()),std::vector<float>(init_extruders.begin(),init_extruders.end()));
-
-				if (dlg.ShowModal() == wxID_OK) {
-                    std::vector<float> matrix = dlg.get_matrix();
-                    std::vector<float> extruders = dlg.get_extruders();
-                    (m_config->option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
-                    (m_config->option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
-                }
-			}));
-			return sizer;
-		};
-		optgroup->append_line(line);
 
 		optgroup = page->new_optgroup(_(L("Advanced")));
 		optgroup->append_single_option_line("interface_shells");
@@ -779,53 +757,40 @@ void TabPrint::update()
 	}
 
 	bool have_perimeters = m_config->opt_int("perimeters") > 0;
-	std::vector<std::string> vec_enable = {	"extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
-											"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width",
-											"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" };
-	for (auto el : vec_enable)
+	for (auto el : {"extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
+					"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width",
+					"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" })
 		get_field(el)->toggle(have_perimeters);
 
 	bool have_infill = m_config->option<ConfigOptionPercent>("fill_density")->value > 0;
-	vec_enable.resize(0);
-	vec_enable = {	"fill_pattern", "infill_every_layers", "infill_only_where_needed",
-					"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" };
 	// infill_extruder uses the same logic as in Print::extruders()
-	for (auto el : vec_enable)
+	for (auto el : {"fill_pattern", "infill_every_layers", "infill_only_where_needed",
+					"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" })
 		get_field(el)->toggle(have_infill);
 
 	bool have_solid_infill = m_config->opt_int("top_solid_layers") > 0 || m_config->opt_int("bottom_solid_layers") > 0;
-	vec_enable.resize(0);
-	vec_enable = {	"external_fill_pattern", "infill_first", "solid_infill_extruder",
-					"solid_infill_extrusion_width", "solid_infill_speed" };
 	// solid_infill_extruder uses the same logic as in Print::extruders()
-	for (auto el : vec_enable)
+	for (auto el : {"external_fill_pattern", "infill_first", "solid_infill_extruder",
+					"solid_infill_extrusion_width", "solid_infill_speed" })
 		get_field(el)->toggle(have_solid_infill);
 
-	vec_enable.resize(0);
-	vec_enable = {	"fill_angle", "bridge_angle", "infill_extrusion_width",
-					"infill_speed", "bridge_speed" };
-	for (auto el : vec_enable)
+	for (auto el : {"fill_angle", "bridge_angle", "infill_extrusion_width",
+					"infill_speed", "bridge_speed" })
 		get_field(el)->toggle(have_infill || have_solid_infill);
 
 	get_field("gap_fill_speed")->toggle(have_perimeters && have_infill);
 
 	bool have_top_solid_infill = m_config->opt_int("top_solid_layers") > 0;
-	vec_enable.resize(0);
-	vec_enable = { "top_infill_extrusion_width", "top_solid_infill_speed" };
-	for (auto el : vec_enable)
+	for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" })
 		get_field(el)->toggle(have_top_solid_infill);
 
 	bool have_default_acceleration = m_config->opt_float("default_acceleration") > 0;
-	vec_enable.resize(0);
-	vec_enable = {	"perimeter_acceleration", "infill_acceleration",
-					"bridge_acceleration", "first_layer_acceleration" };
-	for (auto el : vec_enable)
+	for (auto el : {"perimeter_acceleration", "infill_acceleration",
+					"bridge_acceleration", "first_layer_acceleration" })
 		get_field(el)->toggle(have_default_acceleration);
 
 	bool have_skirt = m_config->opt_int("skirts") > 0 || m_config->opt_float("min_skirt_length") > 0;
-	vec_enable.resize(0);
-	vec_enable = { "skirt_distance", "skirt_height" };
-	for (auto el : vec_enable)
+	for (auto el : { "skirt_distance", "skirt_height" })
 		get_field(el)->toggle(have_skirt);
 
 	bool have_brim = m_config->opt_float("brim_width") > 0;
@@ -836,18 +801,14 @@ void TabPrint::update()
 	bool have_support_material = m_config->opt_bool("support_material") || have_raft;
 	bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
 	bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
-	vec_enable.resize(0);
-	vec_enable = {	"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
+	for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
 					"support_material_spacing", "support_material_angle", "support_material_interface_layers",
 					"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
-					"support_material_xy_spacing" };
-	for (auto el : vec_enable)
+					"support_material_xy_spacing" })
 		get_field(el)->toggle(have_support_material);
 
-	vec_enable.resize(0);
-	vec_enable = {	"support_material_interface_spacing", "support_material_interface_extruder",
-					"support_material_interface_speed", "support_material_interface_contact_loops" };
-	for (auto el : vec_enable)
+	for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
+					"support_material_interface_speed", "support_material_interface_contact_loops" })
 		get_field(el)->toggle(have_support_material && have_support_interface);
 	get_field("support_material_synchronize_layers")->toggle(have_support_soluble);
 
@@ -856,20 +817,15 @@ void TabPrint::update()
 	get_field("support_material_speed")->toggle(have_support_material || have_brim || have_skirt);
 
 	bool have_sequential_printing = m_config->opt_bool("complete_objects");
-	vec_enable.resize(0);
-	vec_enable = { "extruder_clearance_radius", "extruder_clearance_height" };
-	for (auto el : vec_enable)
+	for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" })
 		get_field(el)->toggle(have_sequential_printing);
 
 	bool have_ooze_prevention = m_config->opt_bool("ooze_prevention");
 	get_field("standby_temperature_delta")->toggle(have_ooze_prevention);
 
 	bool have_wipe_tower = m_config->opt_bool("wipe_tower");
-	vec_enable.resize(0);
-	vec_enable = {	"wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"};
-	for (auto el : vec_enable)
+	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_wipe_tower_btn->Enable(have_wipe_tower);
 
 	m_recommended_thin_wall_thickness_description_line->SetText(
 		from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle)));
@@ -1020,13 +976,10 @@ void TabFilament::update()
 	bool cooling = m_config->opt_bool("cooling", 0);
 	bool fan_always_on = cooling || m_config->opt_bool("fan_always_on", 0);
 
-	std::vector<std::string> vec_enable = { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" };
-	for (auto el : vec_enable)
+	for (auto el : { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" })
 		get_field(el)->toggle(cooling);
 
-	vec_enable.resize(0);
-	vec_enable = { "min_fan_speed", "disable_fan_first_layers" };
-	for (auto el : vec_enable)
+	for (auto el : { "min_fan_speed", "disable_fan_first_layers" })
 		get_field(el)->toggle(fan_always_on);
 }
 
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 234c90765..4f65f1475 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -90,7 +90,6 @@ protected:
 	wxImageList*		m_icons;
 	wxCheckBox*			m_compatible_printers_checkbox;
 	wxButton*			m_compatible_printers_btn;
-    wxButton*           m_wipe_tower_btn;
 
 	int					m_icon_count;
 	std::map<std::string, size_t>	m_icon_index;		// Map from an icon file name to its index in $self->{icons}.

From 1b08bc94f07930a5ebeb659f785b67a7c9eb6115 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 4 Apr 2018 11:13:28 +0200
Subject: [PATCH 069/118] Added g_wiping_dialog_button status update to
 update_frequently_changed_parameters()

---
 xs/src/slic3r/GUI/Tab.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 1d92439f9..534cd3aa9 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -372,6 +372,10 @@ void Tab::update_frequently_changed_parameters()
 
 	bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
 	get_optgroup()->set_value("brim", val);
+
+	m_config->opt_bool("wipe_tower") ?
+		get_wiping_dialog_button()->Show() :
+		get_wiping_dialog_button()->Hide();
 }
 
 void Tab::reload_compatible_printers_widget()

From 597e8650a6f35b5c40b98d5512598066bde2a464 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 4 Apr 2018 12:49:16 +0200
Subject: [PATCH 070/118] Fixed incorrect redraw of Purging volumes button in
 certain cases

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

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 32fac1423..bb0e859e6 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -376,6 +376,7 @@ void Tab::update_frequently_changed_parameters()
 	m_config->opt_bool("wipe_tower") ?
 		get_wiping_dialog_button()->Show() :
 		get_wiping_dialog_button()->Hide();
+    (get_wiping_dialog_button()->GetParent())->Layout();
 }
 
 void Tab::reload_compatible_printers_widget()

From 290e3e66c01dbb181ab05a6fa551ec3b66dd7220 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 4 Apr 2018 14:41:23 +0200
Subject: [PATCH 071/118] Merge correction

---
 xs/src/slic3r/GUI/GUI.cpp | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 3fc3bfba2..80a6427d5 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -716,8 +716,10 @@ ConfigOptionsGroup* get_optgroup()
 }
 
 
-wxButton* get_wiping_dialog_button(){
+wxButton* get_wiping_dialog_button()
+{
 	return g_wiping_dialog_button;
+}
 
 wxWindow* export_option_creator(wxWindow* parent)
 {
@@ -762,6 +764,9 @@ int get_export_option(wxFileDialog* dlg)
     }
 
     return 0;
+
 }
 
-} }
+
+} // namespace GUI
+} // namespace Slic3r

From 706dd7020fedbd0aa8e0e90642fcf508350ec604 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Wed, 4 Apr 2018 14:59:31 +0200
Subject: [PATCH 072/118] New extruder temperature is only set when it differs
 from the old one

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index e9523b731..682845135 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -260,10 +260,13 @@ public:
 	// Set extruder temperature, don't wait by default.
 	Writer& set_extruder_temp(int temperature, bool wait = false)
 	{
-		char buf[128];
-		sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
-		m_gcode += buf;
-		return *this;
+        if (temperature != current_temp) {
+            char buf[128];
+            sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
+            m_gcode += buf;
+            current_temp = temperature;
+        }
+        return *this;
 	};
 
     // Wait for a period of time (seconds).
@@ -377,6 +380,7 @@ 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)

From 1e185dacc466dec21fd3663999dde60be6f604ee Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 5 Apr 2018 09:02:03 +0200
Subject: [PATCH 073/118] Out of bed detection - GUI buttons disabled after
 object load if model detected as out of bed

---
 lib/Slic3r/GUI/Plater.pm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 14c2d66ae..ecac430a4 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -105,7 +105,6 @@ sub new {
         $self->{btn_reslice}->Enable($enable);
         $self->{btn_print}->Enable($enable);
         $self->{btn_send_gcode}->Enable($enable);
-        $self->{btn_export_stl}->Enable($enable);    
     };
     
     # Initialize 3D plater
@@ -1915,7 +1914,8 @@ sub object_list_changed {
     }
 
     my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file};
-    my $method = ($have_objects && ! $export_in_progress) ? 'Enable' : 'Disable';
+    my $model_fits = $self->{model}->fits_print_volume($self->{config});
+    my $method = ($have_objects && ! $export_in_progress && $model_fits) ? 'Enable' : 'Disable';
     $self->{"btn_$_"}->$method
         for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode);
 }

From 6af45362ce85bf4debb3f4483d74a0e973ffb377 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 5 Apr 2018 10:44:31 +0200
Subject: [PATCH 074/118] Purging volumes button is only shown when the wipe
 tower is enabled and we have single extruder MM printer with more than one
 extruder

---
 xs/src/slic3r/GUI/Tab.cpp | 32 ++++++++++++++++++++++----------
 xs/src/slic3r/GUI/Tab.hpp |  1 +
 2 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index bb0e859e6..e05dda8c4 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -335,15 +335,28 @@ void Tab::on_value_change(std::string opt_key, boost::any value)
 		bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
 		get_optgroup()->set_value("brim", val);
 	}
-	if (opt_key == "wipe_tower"){
-		m_config->opt_bool("wipe_tower") ? 
-			get_wiping_dialog_button()->Show() : 
-			get_wiping_dialog_button()->Hide();
-	}
-		
+
+    if (opt_key == "wipe_tower" || opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" )
+        update_wiping_button_visibility();
+
 	update();
 }
 
+
+// Show/hide the 'purging volumes' button
+void Tab::update_wiping_button_visibility() {
+    bool wipe_tower_enabled = dynamic_cast<ConfigOptionBool*>(  (m_preset_bundle->prints.get_edited_preset().config  ).option("wipe_tower"))->value;
+    bool multiple_extruders = dynamic_cast<ConfigOptionFloats*>((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1;
+    bool single_extruder_mm = dynamic_cast<ConfigOptionBool*>(  (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value;
+
+    if (wipe_tower_enabled && multiple_extruders && single_extruder_mm)
+        get_wiping_dialog_button()->Show();
+    else get_wiping_dialog_button()->Hide();
+
+    (get_wiping_dialog_button()->GetParent())->Layout();
+}
+
+
 // Call a callback to update the selection of presets on the platter:
 // To update the content of the selection boxes,
 // to update the filament colors of the selection boxes,
@@ -373,10 +386,7 @@ void Tab::update_frequently_changed_parameters()
 	bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
 	get_optgroup()->set_value("brim", val);
 
-	m_config->opt_bool("wipe_tower") ?
-		get_wiping_dialog_button()->Show() :
-		get_wiping_dialog_button()->Hide();
-    (get_wiping_dialog_button()->GetParent())->Layout();
+	update_wiping_button_visibility();
 }
 
 void Tab::reload_compatible_printers_widget()
@@ -1062,6 +1072,8 @@ void TabPrinter::build()
 				if (opt_key.compare("extruders_count")==0 || opt_key.compare("single_extruder_multi_material")==0) {
 					extruders_count_changed(extruders_count);
 					update_dirty();
+                    if (opt_key.compare("single_extruder_multi_material")==0) // the single_extruder_multimaterial was added to force pages
+                        on_value_change(opt_key, value);                      // rebuild - let's make sure the on_value_change is not skipped
 				}
 				else {
 					update_dirty();
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 4f65f1475..f4aafd184 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -174,6 +174,7 @@ public:
 protected:
 	void			on_presets_changed();
 	void			update_frequently_changed_parameters();
+    void            update_wiping_button_visibility();
 };
 
 //Slic3r::GUI::Tab::Print;

From 76beaa6421ca1391c1463ce68d3bc21615f68e67 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 5 Apr 2018 12:52:29 +0200
Subject: [PATCH 075/118] Objects colored by extruder color

---
 lib/Slic3r/GUI/3DScene.pm                 |  5 ++
 lib/Slic3r/GUI/Plater/3D.pm               |  2 +
 lib/Slic3r/GUI/Plater/ObjectCutDialog.pm  |  1 +
 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm |  1 +
 xs/src/slic3r/GUI/3DScene.cpp             | 96 ++++++++++++++++++++++-
 xs/src/slic3r/GUI/3DScene.hpp             | 10 +++
 xs/src/slic3r/GUI/PresetBundle.cpp        |  2 +-
 xs/src/slic3r/GUI/PresetBundle.hpp        |  2 +
 xs/xsp/GUI_3DScene.xsp                    |  1 +
 9 files changed, 118 insertions(+), 2 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index c05de06c6..a89ed0888 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -1677,6 +1677,11 @@ sub draw_warning {
     }
 }
 
+sub update_volumes_colors_by_extruder {
+    my ($self, $config) = @_;    
+    $self->volumes->update_colors_by_extruder($config);
+}
+
 sub opengl_info
 {
     my ($self, %params) = @_;
diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index 09cc02930..36d55d27b 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -216,6 +216,8 @@ sub reload_scene {
         }
     }
     
+    $self->update_volumes_colors_by_extruder($self->{config});
+    
     # checks for geometry outside the print volume to render it accordingly
     if (scalar @{$self->volumes} > 0)
     {
diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 7712bd01d..0b5312b30 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -247,6 +247,7 @@ sub _update {
                 $self->{cut_options}{z},
                 [@expolygons],
             );
+            $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config});
             $self->{canvas}->Render;
         }
     }
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 8a8e6064c..424d88d69 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -162,6 +162,7 @@ sub new {
         $canvas->load_object($self->{model_object}, undef, undef, [0]);
         $canvas->set_auto_bed_shape;
         $canvas->SetSize([500,700]);
+        $canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
         $canvas->zoom_to_volumes;
     }
     
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index a459b7cbf..1ce3d76bf 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -9,6 +9,7 @@
 #include "../../libslic3r/GCode/PreviewData.hpp"
 #include "../../libslic3r/Print.hpp"
 #include "../../libslic3r/Slicing.hpp"
+#include "../../slic3r/GUI/PresetBundle.hpp"
 #include "GCode/Analyzer.hpp"
 
 #include <stdio.h>
@@ -381,6 +382,15 @@ std::vector<int> GLVolumeCollection::load_object(
     std::vector<int> volumes_idx;
     for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) {
         const ModelVolume *model_volume = model_object->volumes[volume_idx];
+
+        int extruder_id = -1;
+        if (!model_volume->modifier)
+        {
+            extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
+            if (extruder_id == 0)
+                extruder_id = model_object->config.has("extruder") ? model_object->config.option("extruder")->getInt() : 0;
+        }
+
         for (int instance_idx : instance_idxs) {
             const ModelInstance *instance = model_object->instances[instance_idx];
             TriangleMesh mesh = model_volume->mesh;
@@ -410,8 +420,14 @@ std::vector<int> GLVolumeCollection::load_object(
                 v.drag_group_id = obj_idx * 1000;
             else if (drag_by == "instance")
                 v.drag_group_id = obj_idx * 1000 + instance_idx;
-            if (! model_volume->modifier)
+
+            if (!model_volume->modifier)
+            {
                 v.layer_height_texture = layer_height_texture;
+                if (extruder_id != -1)
+                    v.extruder_id = extruder_id;
+            }
+            v.is_modifier = model_volume->modifier;
         }
     }
     
@@ -438,6 +454,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
     v.composite_id = obj_idx * 1000000;
     v.select_group_id = obj_idx * 1000000;
     v.drag_group_id = obj_idx * 1000;
+    v.is_wipe_tower = true;
     return int(this->volumes.size() - 1);
 }
 
@@ -642,6 +659,83 @@ void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config,
     }
 }
 
+void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* config)
+{
+    static const float inv_255 = 1.0f / 255.0f;
+
+    struct Color
+    {
+        std::string text;
+        unsigned char rgb[3];
+
+        Color()
+            : text("")
+        {
+            rgb[0] = 255;
+            rgb[1] = 255;
+            rgb[2] = 255;
+        }
+
+        void set(const std::string& text, unsigned char* rgb)
+        {
+            this->text = text;
+            ::memcpy((void*)this->rgb, (const void*)rgb, 3 * sizeof(unsigned char));
+        }
+    };
+
+    if (config == nullptr)
+        return;
+
+    const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
+    if (extruders_opt == nullptr)
+        return;
+
+    const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
+    if (filamemts_opt == nullptr)
+        return;
+
+    unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
+    if (colors_count == 0)
+        return;
+
+    std::vector<Color> colors(colors_count);
+
+    unsigned char rgb[3];
+    for (unsigned int i = 0; i < colors_count; ++i)
+    {
+        const std::string& txt_color = config->opt_string("extruder_colour", i);
+        if (PresetBundle::parse_color(txt_color, rgb))
+        {
+            colors[i].set(txt_color, rgb);
+        }
+        else
+        {
+            const std::string& txt_color = config->opt_string("filament_colour", i);
+            if (PresetBundle::parse_color(txt_color, rgb))
+                colors[i].set(txt_color, rgb);
+        }
+    }
+
+    for (GLVolume* volume : volumes)
+    {
+        if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower)
+            continue;
+
+        int extruder_id = volume->extruder_id - 1;
+        if ((extruder_id < 0) || ((unsigned int)colors.size() <= extruder_id))
+            extruder_id = 0;
+
+        const Color& color = colors[extruder_id];
+        if (!color.text.empty())
+        {
+            for (int i = 0; i < 3; ++i)
+            {
+                volume->color[i] = (float)color.rgb[i] * inv_255;
+            }
+        }
+    }
+}
+
 std::vector<double> GLVolumeCollection::get_current_print_zs() const
 {
     // Collect layer top positions of all volumes.
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index 0fd31d8d6..6486a7f0e 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -246,12 +246,15 @@ public:
         composite_id(-1),
         select_group_id(-1),
         drag_group_id(-1),
+        extruder_id(0),
         selected(false),
         is_active(true),
         zoom_to_volumes(true),
         outside_printer_detection_enabled(true),
         is_outside(false),
         hover(false),
+        is_modifier(false),
+        is_wipe_tower(false),
         tverts_range(0, size_t(-1)),
         qverts_range(0, size_t(-1))
     {
@@ -287,6 +290,8 @@ public:
     int                 select_group_id;
     // An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance.
     int                 drag_group_id;
+    // An ID containing the extruder ID (used to select color).
+    int                 extruder_id;
     // Is this object selected?
     bool                selected;
     // Whether or not this volume is active for rendering
@@ -299,6 +304,10 @@ public:
     bool                is_outside;
     // Boolean: Is mouse over this object?
     bool                hover;
+    // Wheter or not this volume has been generated from a modifier
+    bool                 is_modifier;
+    // Wheter or not this volume has been generated from the wipe tower
+    bool                 is_wipe_tower;
 
     // Interleaved triangles & normals with indexed triangles & quads.
     GLIndexedVertexArray        indexed_vertex_array;
@@ -417,6 +426,7 @@ public:
     }
 
     void update_outside_state(const DynamicPrintConfig* config, bool all_inside);
+    void update_colors_by_extruder(const DynamicPrintConfig* config);
 
     // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
     std::vector<double> get_current_print_zs() const;
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index bf79c6562..ce56d4cb1 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -788,7 +788,7 @@ static inline int hex_digit_to_int(const char c)
         (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
 }
 
-static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out)
+bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out)
 {
     rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
     if (scolor.size() != 7 || scolor.front() != '#')
diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp
index e6aea206d..e25647c7b 100644
--- a/xs/src/slic3r/GUI/PresetBundle.hpp
+++ b/xs/src/slic3r/GUI/PresetBundle.hpp
@@ -98,6 +98,8 @@ public:
     // preset if the current print or filament preset is not compatible.
     void                        update_compatible_with_printer(bool select_other_if_incompatible);
 
+    static bool parse_color(const std::string &scolor, unsigned char *rgb_out);
+
 private:
     // Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path.
     // and the external config is just referenced, not stored into user profile directory.
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 25aa6b81a..ce773adeb 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -105,6 +105,7 @@
 
     void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z);
     void update_outside_state(DynamicPrintConfig* config, bool all_inside);
+    void update_colors_by_extruder(DynamicPrintConfig* config);
 
     bool move_volume_up(int idx)
         %code%{ 

From 3a61833d131ac3ae0181f8bc34212d63133c7270 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 5 Apr 2018 15:55:50 +0200
Subject: [PATCH 076/118] Update view in object's setting dialog when changing
 extruder

---
 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 424d88d69..c7b9049d8 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -88,7 +88,7 @@ sub new {
     $self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
     
     # part settings panel
-    $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; });
+    $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; });
     my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
     $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
 
@@ -479,6 +479,7 @@ sub _parts_changed {
         $self->{canvas}->reset_objects;
         $self->{canvas}->load_object($self->{model_object});
         $self->{canvas}->zoom_to_volumes;
+        $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
         $self->{canvas}->Render;
     }
 }
@@ -509,6 +510,25 @@ sub PartSettingsChanged {
     return $self->{part_settings_changed};
 }
 
+sub _update_canvas {
+    my ($self) = @_;
+    
+    if ($self->{canvas}) {
+        $self->{canvas}->reset_objects;
+        $self->{canvas}->load_object($self->{model_object});
+
+        # restore selection, if any
+        if (my $itemData = $self->get_selection) {
+            if ($itemData->{type} eq 'volume') {
+                $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1);
+            }
+        }
+                
+        $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
+        $self->{canvas}->Render;
+    }
+}
+
 sub _update {
     my ($self) = @_;
     my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z});
@@ -529,6 +549,7 @@ sub _update {
     push @objects, $self->{model_object};
     $self->{canvas}->reset_objects;
     $self->{canvas}->load_object($_, undef, [0]) for @objects;
+    $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
     $self->{canvas}->Render;
 }
 

From cb9937cde4c4d65fc4e7f8c71cf750f3a5e63974 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 6 Apr 2018 12:02:52 +0200
Subject: [PATCH 077/118] Corrected wipe tower comments in GCode

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 682845135..dd75e03ed 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -460,7 +460,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 {
 
 	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
-	this->m_num_layer_changes 	= 0;
 	this->m_current_tool 		= tools.front();
     
     // The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
@@ -574,7 +573,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
 		.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))
 		.append(";--------------------\n"
 				"; CP TOOLCHANGE START\n")
-		.comment_with_value(" toolchange #", m_num_tool_changes)
+		.comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based
 		.comment_material(m_filpar[m_current_tool].material)
 		.append(";--------------------\n")
 		.speed_override(100);
@@ -975,8 +974,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
 		.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
 		.append(";--------------------\n"
 				"; CP EMPTY GRID START\n")
-		// m_num_layer_changes is incremented by set_z, so it is 1 based.
-		.comment_with_value(" layer #", m_num_layer_changes - 1);
+		.comment_with_value(" layer #", m_num_layer_changes + 1);
 
 	// Slow down on the 1st layer.
 	float speed_factor = m_is_first_layer ? 0.5f : 1.f;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 26c40fbc9..175de0276 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -121,8 +121,12 @@ public:
 		m_print_brim = is_first_layer;
 		m_depth_traversed  = 0.f;
 		m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
-		
-		++ m_num_layer_changes;
+		if (is_first_layer) {
+            this->m_num_layer_changes 	= 0;
+            this->m_num_tool_changes 	= 0;
+        }
+        else
+            ++ m_num_layer_changes;
 		
 		// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
 		m_extrusion_flow = extrusion_flow(layer_height);

From e4b767e8403c1c860c36598e822b02d6c2bbfc2d Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 6 Apr 2018 13:37:00 +0200
Subject: [PATCH 078/118] Experiment with own ComboCtrl for preset_choice.

---
 xs/src/slic3r/GUI/Field.cpp        |  3 +-
 xs/src/slic3r/GUI/Tab.cpp          | 47 ++++++++++++++++++++++++++++++
 xs/src/slic3r/GUI/Tab.hpp          |  2 ++
 xs/src/slic3r/GUI/wxExtensions.cpp | 44 ++++++++++++++++++++++++++++
 xs/src/slic3r/GUI/wxExtensions.hpp | 24 +++++++++++++++
 5 files changed, 118 insertions(+), 2 deletions(-)

diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp
index fee08e27e..a7fd8d29e 100644
--- a/xs/src/slic3r/GUI/Field.cpp
+++ b/xs/src/slic3r/GUI/Field.cpp
@@ -173,8 +173,7 @@ namespace Slic3r { namespace GUI {
 
 		temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
 		{
-//			on_kill_focus(e);
-			e.Skip();
+			e.Skip();//	on_kill_focus(e);
 			temp->GetToolTip()->Enable(true);
 		}), temp->GetId());
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index babff7d80..3935dda5a 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -20,6 +20,7 @@
 #include <wx/filedlg.h>
 
 #include <boost/algorithm/string/predicate.hpp>
+#include "wxExtensions.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -38,6 +39,50 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 	// preset chooser
 	m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(270, -1), 0, 0,wxCB_READONLY);
 
+	m_cc_presets_choice = new wxComboCtrl(panel, wxID_ANY, L("Presets"), wxDefaultPosition, wxSize(270, -1), wxCB_READONLY);
+	wxDataViewTreeCtrlComboPopup* popup = new wxDataViewTreeCtrlComboPopup;
+	if (popup != nullptr)
+	{
+		// FIXME If the following line is removed, the combo box popup list will not react to mouse clicks.
+		//  On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
+// 		comboCtrl->UseAltPopupWindow();
+// 		comboCtrl->EnablePopupAnimation(false);
+		m_cc_presets_choice->SetPopupControl(popup);
+		popup->SetStringValue(from_u8("Text1"));
+
+		popup->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this, popup](wxCommandEvent& evt)
+		{
+			auto selected = popup->GetItemText(popup->GetSelection());
+			if (selected != _(L("System presets")) && selected != _(L("Default presets")))
+				m_cc_presets_choice->SetText(selected);
+//			popup->OnDataViewTreeCtrlSelection(evt);
+		});
+		popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
+		popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
+
+		auto icons = new wxImageList(16, 16, true, 1);
+		popup->SetImageList(icons);
+		icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG));
+		icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG));
+
+		Freeze();
+
+		// get label of the currently selected item
+		auto selected = popup->GetItemText(popup->GetSelection());
+		auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets")));
+		auto tree_node1 = popup->AppendItem(root_sys, _("Sys1"), 0);
+		auto tree_node2 = popup->AppendContainer(root_sys, _("Sys2"), 0);
+		auto tree_node2_1 = popup->AppendItem(tree_node2, _("Sys2_1"), 0);
+
+		auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
+		auto tree_node01 = popup->AppendContainer(root_def, _("Def1"), 0);
+		auto tree_node02 = popup->AppendContainer(root_def, _("Def2"), 0);
+		auto tree_node02_1 = popup->AppendItem(tree_node02, _("Def2_1"), 0);
+
+		Thaw();
+	}
+
+
 	auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
 	//buttons
@@ -82,6 +127,8 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 	m_hsizer->AddSpacer(64);
 	m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
 	m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
+	m_hsizer->AddSpacer(64);
+	m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
 
 	//Horizontal sizer to hold the tree and the selected page.
 	m_hsizer = new wxBoxSizer(wxHORIZONTAL);
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index f39ff728c..075eab218 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -21,6 +21,7 @@
 #include <wx/treectrl.h>
 #include <wx/imaglist.h>
 #include <wx/statbox.h>
+#include <wx/dataview.h>
 
 #include <map>
 #include <vector>
@@ -95,6 +96,7 @@ protected:
 	wxButton*			m_compatible_printers_btn;
 	wxButton*			m_undo_btn;
 	wxButton*			m_undo_to_sys_btn;
+	wxComboCtrl*		m_cc_presets_choice;
 
 	int					m_icon_count;
 	std::map<std::string, size_t>	m_icon_index;		// Map from an icon file name to its index
diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp
index 1ebd7979e..c4dfb7e10 100644
--- a/xs/src/slic3r/GUI/wxExtensions.cpp
+++ b/xs/src/slic3r/GUI/wxExtensions.cpp
@@ -109,3 +109,47 @@ void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
         ProcessEvent(event);
     }
 }
+
+
+// ***  wxDataViewTreeCtrlComboPopup  ***
+
+const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270;
+const unsigned int wxDataViewTreeCtrlComboPopup::DefaultHeight = 200;
+const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22;
+
+bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent)
+{
+	return wxDataViewTreeCtrl::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0), wxSize(270, -1));
+}
+
+wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
+{
+	// matches owner wxComboCtrl's width
+	// and sets height dinamically in dependence of contained items count
+	return wxSize(DefaultWidth, DefaultHeight);
+}
+
+void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt)
+{
+	// filters out all the keys which are not working properly
+	if (evt.GetKeyCode() == WXK_UP)
+	{
+		return;
+	}
+	else if (evt.GetKeyCode() == WXK_DOWN)
+	{
+		return;
+	}
+	else
+	{
+		evt.Skip();
+		return;
+	}
+}
+
+void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& evt)
+{
+	wxComboCtrl* cmb = GetComboCtrl();
+	auto selected = GetItemText(GetSelection());
+	cmb->SetText(selected);
+}
diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp
index e61c17bbc..bba412cfd 100644
--- a/xs/src/slic3r/GUI/wxExtensions.hpp
+++ b/xs/src/slic3r/GUI/wxExtensions.hpp
@@ -3,6 +3,7 @@
 
 #include <wx/checklst.h>
 #include <wx/combo.h>
+#include <wx/dataview.h>
 
 class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
 {
@@ -25,4 +26,27 @@ public:
     void OnListBoxSelection(wxCommandEvent& evt);
 };
 
+
+// ***  wxDataViewTreeCtrlComboBox  ***
+
+class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup
+{
+	static const unsigned int DefaultWidth;
+	static const unsigned int DefaultHeight;
+	static const unsigned int DefaultItemHeight;
+
+	wxString	m_text;
+	int			m_cnt_open_items=2;
+
+public:
+	virtual bool		Create(wxWindow* parent);
+	virtual wxWindow*	GetControl() { return this; }
+	virtual void		SetStringValue(const wxString& value) { m_text = value; }
+	virtual wxString	GetStringValue() const { return m_text; }
+	virtual wxSize		GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
+
+	virtual void		OnKeyEvent(wxKeyEvent& evt);
+	void				OnDataViewTreeCtrlSelection(wxCommandEvent& evt);
+};
+
 #endif // slic3r_GUI_wxExtensions_hpp_

From 004f9ba1e5457203057b8cb2dad04c089bd64acf Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 6 Apr 2018 15:42:52 +0200
Subject: [PATCH 079/118] Added update_tab_presets functions to
 m_cc_preset_choise filling

---
 xs/src/slic3r/GUI/Preset.cpp       |  4 ++
 xs/src/slic3r/GUI/Preset.hpp       |  6 +++
 xs/src/slic3r/GUI/Tab.cpp          | 65 ++++++++++++++++++++++--------
 xs/src/slic3r/GUI/Tab.hpp          |  1 +
 xs/src/slic3r/GUI/wxExtensions.cpp | 14 ++++++-
 xs/src/slic3r/GUI/wxExtensions.hpp |  3 +-
 6 files changed, 74 insertions(+), 19 deletions(-)

diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index c437f8b41..d612896a5 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -417,6 +417,10 @@ const Preset* PresetCollection::get_selected_preset_parent() const
     return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset;
 }
 
+const std::string& PresetCollection::get_suffix_modified() {
+	return g_suffix_modified;
+}
+
 // Return a preset by its name. If the preset is active, a temporary copy is returned.
 // If a preset is not found by its name, null is returned.
 Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found)
diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp
index 3634c5dd9..f19f511c3 100644
--- a/xs/src/slic3r/GUI/Preset.hpp
+++ b/xs/src/slic3r/GUI/Preset.hpp
@@ -212,6 +212,12 @@ public:
     // Return the selected preset including the user modifications.
     Preset&         get_edited_preset()         { return m_edited_preset; }
     const Preset&   get_edited_preset() const   { return m_edited_preset; }
+
+	// used to update preset_choice from Tab
+	const std::deque<Preset>&	get_presets()	{ return m_presets; }
+	int						get_idx_selected()	{ return m_idx_selected; }
+	const std::string&		get_suffix_modified();
+
     // Return a preset possibly with modifications.
     const Preset&   default_preset() const      { return m_presets.front(); }
     // Return a preset by an index. If the preset is active, a temporary copy is returned.
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 3935dda5a..37ed8f97f 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -54,32 +54,21 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 		{
 			auto selected = popup->GetItemText(popup->GetSelection());
 			if (selected != _(L("System presets")) && selected != _(L("Default presets")))
+			{
 				m_cc_presets_choice->SetText(selected);
+				std::string selected_string = selected.ToUTF8().data();
+//!				select_preset(selected_string);
+			}				
 //			popup->OnDataViewTreeCtrlSelection(evt);
 		});
+
 		popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
 		popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
 
 		auto icons = new wxImageList(16, 16, true, 1);
 		popup->SetImageList(icons);
-		icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG));
 		icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG));
-
-		Freeze();
-
-		// get label of the currently selected item
-		auto selected = popup->GetItemText(popup->GetSelection());
-		auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets")));
-		auto tree_node1 = popup->AppendItem(root_sys, _("Sys1"), 0);
-		auto tree_node2 = popup->AppendContainer(root_sys, _("Sys2"), 0);
-		auto tree_node2_1 = popup->AppendItem(tree_node2, _("Sys2_1"), 0);
-
-		auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
-		auto tree_node01 = popup->AppendContainer(root_def, _("Def1"), 0);
-		auto tree_node02 = popup->AppendContainer(root_def, _("Def2"), 0);
-		auto tree_node02_1 = popup->AppendItem(tree_node02, _("Def2_1"), 0);
-
-		Thaw();
+		icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG));
 	}
 
 
@@ -525,11 +514,13 @@ void Tab::update_dirty(){
 	m_presets->update_dirty_ui(m_presets_choice);
 	on_presets_changed();	
 	update_changed_ui();
+//	update_dirty_presets(m_cc_presets_choice);
 }
 
 void Tab::update_tab_ui()
 {
 	m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets);
+	update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets);
 }
 
 // Load a provied DynamicConfig into the tab, modifying the active preset.
@@ -2141,6 +2132,46 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
 	return sizer; 
 }
 
+void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
+{
+	if (ui == nullptr)
+		return;
+	ui->Freeze();
+	ui->Clear();
+	auto presets = m_presets->get_presets();
+	auto idx_selected = m_presets->get_idx_selected();
+	auto suffix_modified = m_presets->get_suffix_modified();
+	int icon_compatible = 0;
+	int icon_incompatible = 1;
+	int cnt_items = 0;
+
+	wxDataViewTreeCtrlComboPopup* popup = wxDynamicCast(m_cc_presets_choice->GetPopupControl(), wxDataViewTreeCtrlComboPopup);
+	if (popup != nullptr)
+	{
+		popup->DeleteAllItems();
+		auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets")));
+		auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
+
+		for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) {
+			const Preset &preset = presets[i];
+			if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected))
+				continue;
+
+			auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str());
+
+			auto item = popup->AppendItem(preset.is_system ? root_sys : root_def, preset_name, 
+										  preset.is_compatible ? icon_compatible : icon_incompatible);
+			cnt_items++;
+			if (i == idx_selected){
+				popup->Select(item);
+				m_cc_presets_choice->SetText(preset_name);
+			}
+		}
+	}
+	popup->SetItemsCnt(cnt_items);
+	ui->Thaw();
+}
+
 void Page::reload_config()
 {
 	for (auto group : m_optgroups)
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 075eab218..5e2eec80d 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -196,6 +196,7 @@ public:
 protected:
 	void			on_presets_changed();
 	void			update_frequently_changed_parameters();
+	void			update_tab_presets(wxComboCtrl* ui, bool show_incompatible);
 };
 
 //Slic3r::GUI::Tab::Print;
diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp
index c4dfb7e10..d9f4e520a 100644
--- a/xs/src/slic3r/GUI/wxExtensions.cpp
+++ b/xs/src/slic3r/GUI/wxExtensions.cpp
@@ -126,7 +126,19 @@ wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeigh
 {
 	// matches owner wxComboCtrl's width
 	// and sets height dinamically in dependence of contained items count
-	return wxSize(DefaultWidth, DefaultHeight);
+	wxComboCtrl* cmb = GetComboCtrl();
+	if (cmb != nullptr)
+	{
+		wxSize size = GetComboCtrl()->GetSize();
+		if (m_cnt_open_items > 0)
+			size.SetHeight(m_cnt_open_items * DefaultItemHeight);
+		else
+			size.SetHeight(DefaultHeight);
+
+		return size;
+	}
+	else
+		return wxSize(DefaultWidth, DefaultHeight);
 }
 
 void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt)
diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp
index bba412cfd..701b12dba 100644
--- a/xs/src/slic3r/GUI/wxExtensions.hpp
+++ b/xs/src/slic3r/GUI/wxExtensions.hpp
@@ -36,7 +36,7 @@ class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPop
 	static const unsigned int DefaultItemHeight;
 
 	wxString	m_text;
-	int			m_cnt_open_items=2;
+	int			m_cnt_open_items{0};
 
 public:
 	virtual bool		Create(wxWindow* parent);
@@ -47,6 +47,7 @@ public:
 
 	virtual void		OnKeyEvent(wxKeyEvent& evt);
 	void				OnDataViewTreeCtrlSelection(wxCommandEvent& evt);
+	void				SetItemsCnt(int cnt) { m_cnt_open_items = cnt; }
 };
 
 #endif // slic3r_GUI_wxExtensions_hpp_

From e33db203d8b1819cc452e173c39003c33cef5f75 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 9 Apr 2018 09:55:24 +0200
Subject: [PATCH 080/118] Next try to use wxDataViewTreeCtrlComboPopup on macOS
 and Linux:

Added: m_cc_presets_choice->UseAltPopupWindow();
       m_cc_presets_choice->EnablePopupAnimation(false);
like for wxCheckListBoxComboPopup
---
 xs/src/slic3r/GUI/Tab.cpp | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 37ed8f97f..ee8d1261c 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -45,8 +45,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 	{
 		// FIXME If the following line is removed, the combo box popup list will not react to mouse clicks.
 		//  On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
-// 		comboCtrl->UseAltPopupWindow();
-// 		comboCtrl->EnablePopupAnimation(false);
+		m_cc_presets_choice->UseAltPopupWindow();
+
+		m_cc_presets_choice->EnablePopupAnimation(false);
 		m_cc_presets_choice->SetPopupControl(popup);
 		popup->SetStringValue(from_u8("Text1"));
 
@@ -57,7 +58,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 			{
 				m_cc_presets_choice->SetText(selected);
 				std::string selected_string = selected.ToUTF8().data();
-//!				select_preset(selected_string);
+				select_preset(selected_string);
 			}				
 //			popup->OnDataViewTreeCtrlSelection(evt);
 		});
@@ -2159,8 +2160,14 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 
 			auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str());
 
-			auto item = popup->AppendItem(preset.is_system ? root_sys : root_def, preset_name, 
-										  preset.is_compatible ? icon_compatible : icon_incompatible);
+			wxDataViewItem item;
+			if (preset.is_system || preset.is_default)
+				item = popup->AppendItem(preset.is_system ? root_sys : root_def, preset_name, 
+										 preset.is_compatible ? icon_compatible : icon_incompatible);
+			else {
+				
+			}
+
 			cnt_items++;
 			if (i == idx_selected){
 				popup->Select(item);
@@ -2168,7 +2175,7 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 			}
 		}
 	}
-	popup->SetItemsCnt(cnt_items);
+	popup->SetItemsCnt(cnt_items+2);
 	ui->Thaw();
 }
 

From affaeef2ab18972374d571260cba9c34d578eee7 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 9 Apr 2018 12:41:25 +0200
Subject: [PATCH 081/118] Default presets are visible only if it's selected
 from Preferences

---
 xs/src/slic3r/GUI/Tab.cpp          | 19 +++++++++++++------
 xs/src/slic3r/GUI/wxExtensions.cpp |  4 ++--
 xs/src/slic3r/GUI/wxExtensions.hpp |  2 +-
 3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index ee8d1261c..5f727830f 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -45,9 +45,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 	{
 		// FIXME If the following line is removed, the combo box popup list will not react to mouse clicks.
 		//  On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
-		m_cc_presets_choice->UseAltPopupWindow();
+//		m_cc_presets_choice->UseAltPopupWindow();
 
-		m_cc_presets_choice->EnablePopupAnimation(false);
+//		m_cc_presets_choice->EnablePopupAnimation(false);
 		m_cc_presets_choice->SetPopupControl(popup);
 		popup->SetStringValue(from_u8("Text1"));
 
@@ -2150,8 +2150,11 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 	if (popup != nullptr)
 	{
 		popup->DeleteAllItems();
+		auto root_1 = popup->AppendContainer(wxDataViewItem(0), _(L("Root 1")));
 		auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets")));
-		auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
+		auto show_def = get_app_config()->get("no_defaults")[0] != '1';
+		wxDataViewItem root_def;
+		if (show_def) root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
 
 		for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) {
 			const Preset &preset = presets[i];
@@ -2161,11 +2164,15 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 			auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str());
 
 			wxDataViewItem item;
-			if (preset.is_system || preset.is_default)
-				item = popup->AppendItem(preset.is_system ? root_sys : root_def, preset_name, 
+			if (preset.is_system)
+				item = popup->AppendItem(root_sys, preset_name, 
+										 preset.is_compatible ? icon_compatible : icon_incompatible);
+			else if (show_def && preset.is_default)
+				item = popup->AppendItem(root_def, preset_name, 
 										 preset.is_compatible ? icon_compatible : icon_incompatible);
 			else {
-				
+				item = popup->AppendItem(root_1, preset_name, 
+										 preset.is_compatible ? icon_compatible : icon_incompatible);				
 			}
 
 			cnt_items++;
diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp
index d9f4e520a..666da7f8a 100644
--- a/xs/src/slic3r/GUI/wxExtensions.cpp
+++ b/xs/src/slic3r/GUI/wxExtensions.cpp
@@ -121,7 +121,7 @@ bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent)
 {
 	return wxDataViewTreeCtrl::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0), wxSize(270, -1));
 }
-
+/*
 wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
 {
 	// matches owner wxComboCtrl's width
@@ -140,7 +140,7 @@ wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeigh
 	else
 		return wxSize(DefaultWidth, DefaultHeight);
 }
-
+*/
 void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt)
 {
 	// filters out all the keys which are not working properly
diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp
index 701b12dba..ed8bb9276 100644
--- a/xs/src/slic3r/GUI/wxExtensions.hpp
+++ b/xs/src/slic3r/GUI/wxExtensions.hpp
@@ -43,7 +43,7 @@ public:
 	virtual wxWindow*	GetControl() { return this; }
 	virtual void		SetStringValue(const wxString& value) { m_text = value; }
 	virtual wxString	GetStringValue() const { return m_text; }
-	virtual wxSize		GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
+//	virtual wxSize		GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
 
 	virtual void		OnKeyEvent(wxKeyEvent& evt);
 	void				OnDataViewTreeCtrlSelection(wxCommandEvent& evt);

From 9ebff9ce0046438ff4362e73996867714c367a0d Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 9 Apr 2018 13:35:39 +0200
Subject: [PATCH 082/118] Bugfix: a sign mistake was causing unnecessary travel
 moves

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

diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index dd75e03ed..ad7d91c50 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -72,7 +72,7 @@ public:
 		{ m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
 
 	Writer&				 set_y_shift(float shift) {
-        m_current_pos.y += shift-m_y_shift;
+        m_current_pos.y -= shift-m_y_shift;
         m_y_shift = shift;
         return (*this);
     }
@@ -112,8 +112,8 @@ public:
 
 
 		// For rotated wipe tower, transform position to printer coordinates
-		WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
-		WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));
+		WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we are
+		WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg));                               // this is where we want to go
 
 		if (! m_preview_suppressed && e > 0.f && len > 0.) {
 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
@@ -127,14 +127,14 @@ public:
 		}
 
 		m_gcode += "G1";
-		if (rot.x != rotated_current_pos.x)
-			m_gcode += set_format_X(rot.x);
-		if (rot.y != rotated_current_pos.y)
+		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)
+            m_current_pos.x = x;
+        }
+		if (rot.y != rotated_current_pos.y) {
 			m_gcode += set_format_Y(rot.y);
-			
-		// Transform current position back to wipe tower coordinates (was updated by set_format_X)
-		m_current_pos.x = x;
-		m_current_pos.y = y;
+            m_current_pos.y = y;
+        }
 
 		if (e != 0.f)
 			m_gcode += set_format_E(e);
@@ -179,16 +179,20 @@ public:
         corners[1] = WipeTower::xy(ld,width,0.f);
         corners[2] = WipeTower::xy(ld,width,height);
         corners[3] = WipeTower::xy(ld,0.f,height);
-        int index_of_closest = (x()-ld.x < ld.x+width-x() ? 0 : 1);
-        if (y()-ld.y > ld.y+height-x())   // closer to the top
-            index_of_closest += (index_of_closest==0 ? 3 : 1);
-        travel(corners[index_of_closest].x,y(),f);      // travel to the closest corner
-        travel(x(),corners[index_of_closest].y,f);
+        int index_of_closest = 0;
+        if (x()-ld.x > ld.x+width-x())    // closer to the right
+            index_of_closest = 1;
+        if (y()-ld.y > ld.y+height-y())   // closer to the top
+            index_of_closest = (index_of_closest==0 ? 3 : 2);
+
+        travel(corners[index_of_closest].x, y());      // travel to the closest corner
+        travel(x(),corners[index_of_closest].y);
+
         int i = index_of_closest;
         do {
             ++i;
             if (i==4) i=0;
-            this->extrude(corners[i]);
+            extrude(corners[i]);
         } while (i != index_of_closest);
         return (*this);
     }
@@ -611,7 +615,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
         else {
             writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
             if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
-                writer.travel(m_wipe_tower_pos.x + (writer.x()>m_wipe_tower_pos.x + EPSILON ? 0.f : m_wipe_tower_width), writer.y());
+                writer.travel(m_wipe_tower_pos.x + (writer.x()> (m_wipe_tower_pos.x + m_wipe_tower_width) / 2.f ? 0.f : m_wipe_tower_width), writer.y());
             }
         }
     }
@@ -987,6 +991,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
     else
         writer.set_initial_position(fill_box.lu);
 
+
 	box_coordinates box = fill_box;
     for (int i=0;i<2;++i) {
         if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer

From 254592c849326ed552677ff8e923922f678220c9 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 9 Apr 2018 14:20:44 +0200
Subject: [PATCH 083/118] Delete key enabled in Object's Settings Dialog

---
 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index c7b9049d8..47fd8a092 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -401,14 +401,17 @@ sub on_tree_key_down {
     my ($self, $event) = @_;
     my $keycode = $event->GetKeyCode;    
     # Wx >= 0.9911
-    if (defined(&Wx::TreeEvent::GetKeyEvent) && 
-        ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL)) {
-        if ($keycode == WXK_UP) {
-            $event->Skip;
-            $self->on_btn_move_up;
-        } elsif ($keycode == WXK_DOWN) {
-            $event->Skip;
-            $self->on_btn_move_down;
+    if (defined(&Wx::TreeEvent::GetKeyEvent)) { 
+        if ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL) {
+            if ($keycode == WXK_UP) {
+                $event->Skip;
+                $self->on_btn_move_up;
+            } elsif ($keycode == WXK_DOWN) {
+                $event->Skip;
+                $self->on_btn_move_down;
+            }
+        } elsif ($keycode == WXK_DELETE) {
+            $self->on_btn_delete;
         }
     }
 }

From 388deb71ab5d03116ef5ec1266c39eea8fb51520 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Mon, 9 Apr 2018 14:41:55 +0200
Subject: [PATCH 084/118] Adapt settings label colors to light vs dark UI
 themes

---
 xs/src/slic3r/GUI/GUI.cpp | 38 ++++++++++++++++++++++++++++++++++----
 xs/src/slic3r/GUI/GUI.hpp |  6 ++++--
 xs/src/slic3r/GUI/Tab.cpp | 14 +++++++-------
 3 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 3eca4e707..9109e8b5c 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -1,6 +1,7 @@
 #include "GUI.hpp"
 
 #include <assert.h>
+#include <cmath>
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/filesystem.hpp>
@@ -37,6 +38,7 @@
 #include <wx/sizer.h>
 #include <wx/combo.h>
 #include <wx/window.h>
+#include <wx/settings.h>
 
 #include "wxExtensions.hpp"
 
@@ -174,6 +176,8 @@ wxFrame     *g_wxMainFrame  = nullptr;
 wxNotebook  *g_wxTabPanel   = nullptr;
 AppConfig	*g_AppConfig	= nullptr;
 PresetBundle *g_PresetBundle= nullptr;
+wxColour    g_color_label_modified;
+wxColour    g_color_label_sys;
 
 std::vector<Tab *> g_tabs_list;
 
@@ -182,9 +186,22 @@ wxLocale*	g_wxLocale;
 std::shared_ptr<ConfigOptionsGroup>	m_optgroup;
 double m_brim_width = 0.0;
 
+static void init_label_colours()
+{
+	auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+	if (luma >= 128) {
+		g_color_label_modified = wxColour(253, 88, 0);
+		g_color_label_sys = wxColour(26, 132, 57);
+	} else {
+		g_color_label_modified = wxColour(253, 111, 40);
+		g_color_label_sys = wxColour(115, 220, 103);
+	}
+}
+
 void set_wxapp(wxApp *app)
 {
     g_wxApp = app;
+    init_label_colours();
 }
 
 void set_main_frame(wxFrame *main_frame)
@@ -514,12 +531,25 @@ wxApp* get_app(){
 	return g_wxApp;
 }
 
-wxColour* get_modified_label_clr(){
-	return new wxColour(253, 88, 0);
+const wxColour& get_modified_label_clr() {
+	return g_color_label_modified;
 }
 
-wxColour* get_sys_label_clr(){
-	return new wxColour(26, 132, 57);
+const wxColour& get_sys_label_clr() {
+	return g_color_label_sys;
+}
+
+unsigned get_colour_approx_luma(const wxColour &colour)
+{
+	double r = colour.Red();
+	double g = colour.Green();
+	double b = colour.Blue();
+
+	std::round(std::sqrt(
+		r * r * .241 +
+		g * g * .691 +
+		b * b * .068
+	));
 }
 
 void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 362b15307..24c3ec3f4 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -79,8 +79,10 @@ void set_preset_bundle(PresetBundle *preset_bundle);
 
 AppConfig*	get_app_config();
 wxApp*		get_app();
-wxColour*	get_modified_label_clr();
-wxColour*	get_sys_label_clr();
+
+const wxColour& get_modified_label_clr();
+const wxColour& get_sys_label_clr();
+unsigned get_colour_approx_luma(const wxColour &colour);
 
 void add_debug_menu(wxMenuBar *menu, int event_language_change);
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index babff7d80..7db35f5cf 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -265,14 +265,14 @@ void Tab::update_changed_ui()
 		bool is_modified_value = true;
 		std::string sys_icon = wxMSW ? "sys_lock.png" : "lock.png";
 		std::string icon = wxMSW ? "action_undo.png" : "arrow_undo.png";
-		wxColour& color = *get_sys_label_clr();
+		wxColour color = get_sys_label_clr();
 		if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) {
 			is_nonsys_value = true;
 			sys_icon = m_nonsys_btn_icon;
 			if(find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end())
-				color = wxSYS_COLOUR_WINDOWTEXT;
+				color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
 			else
-				color = *get_modified_label_clr();
+				color = get_modified_label_clr();
 		}
 		if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end())
 		{
@@ -343,7 +343,7 @@ void Tab::update_sys_ui_after_sel_preset()
 			field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG));
 			field->m_is_nonsys_value = true;
 			if (field->m_Label != nullptr){
-				field->m_Label->SetForegroundColour(wxSYS_COLOUR_WINDOWTEXT);
+				field->m_Label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
 				field->m_Label->Refresh(true);
 			}
 		}
@@ -386,11 +386,11 @@ void Tab::update_changed_tree_ui()
 					break;
 			}
 			if (sys_page)
-				m_treectrl->SetItemTextColour(cur_item, *get_sys_label_clr());
+				m_treectrl->SetItemTextColour(cur_item, get_sys_label_clr());
 			else if (modified_page)
-				m_treectrl->SetItemTextColour(cur_item, *get_modified_label_clr());
+				m_treectrl->SetItemTextColour(cur_item, get_modified_label_clr());
 			else
-				m_treectrl->SetItemTextColour(cur_item, wxSYS_COLOUR_WINDOWTEXT);
+				m_treectrl->SetItemTextColour(cur_item, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
 
 			page->m_is_nonsys_values = !sys_page;
 			page->m_is_modified_values = modified_page;

From bbbb5c9a934e2fd1ad9a65a0af4ce186a43fe133 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 9 Apr 2018 14:49:32 +0200
Subject: [PATCH 085/118] Ramming chart is now drawn with double-buffering

---
 xs/src/slic3r/GUI/RammingChart.cpp | 5 ++++-
 xs/src/slic3r/GUI/RammingChart.hpp | 5 +++--
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
index 87ac9c769..ca76298af 100644
--- a/xs/src/slic3r/GUI/RammingChart.cpp
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -1,4 +1,6 @@
 #include <algorithm>
+#include <wx/dcbuffer.h>
+
 #include "RammingChart.hpp"
 
 
@@ -11,7 +13,8 @@
 wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
 
 
-void Chart::draw(wxDC& dc) {
+void Chart::draw() {
+    wxAutoBufferedPaintDC dc(this); // unbuffered DC caused flickering on win
     dc.SetPen(*wxBLACK_PEN);
     dc.SetBrush(*wxWHITE_BRUSH);
     dc.DrawRectangle(m_rect);
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
index aa1e7acc7..7d3b9a962 100644
--- a/xs/src/slic3r/GUI/RammingChart.hpp
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -16,6 +16,7 @@ public:
     Chart(wxWindow* parent, wxRect rect,const std::vector<std::pair<float,float>>& initial_buttons,int ramming_speed_size, float sampling) :
         wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize())
     {
+        SetBackgroundStyle(wxBG_STYLE_PAINT);
         m_rect = wxRect(wxPoint(50,0),rect.GetSize()-wxSize(50,50));
         visible_area = wxRect2DDouble(0.0, 0.0, sampling*ramming_speed_size, 20.);
         m_buttons.clear();
@@ -36,7 +37,7 @@ public:
     std::vector<float> get_ramming_speed(float sampling) const; //returns sampled ramming speed
     std::vector<std::pair<float,float>> get_buttons() const; // returns buttons position   
     
-    void draw(wxDC& dc);
+    void draw();
     
     void mouse_clicked(wxMouseEvent& event);
     void mouse_right_button_clicked(wxMouseEvent& event);
@@ -44,7 +45,7 @@ public:
     void mouse_double_clicked(wxMouseEvent& event);
     void mouse_left_window(wxMouseEvent&) { m_dragged = nullptr; }        
     void mouse_released(wxMouseEvent&)    { m_dragged = nullptr; }
-    void paint_event(wxPaintEvent&) { wxPaintDC dc(this); draw(dc); }
+    void paint_event(wxPaintEvent&) { draw(); }
     DECLARE_EVENT_TABLE()
     
 

From a8e831e04a433ac41c95937d6e3843e9e51ee881 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Mon, 9 Apr 2018 14:52:57 +0200
Subject: [PATCH 086/118] rm stray file

---
 My Settings | 27 ---------------------------
 1 file changed, 27 deletions(-)
 delete mode 100644 My Settings

diff --git a/My Settings b/My Settings
deleted file mode 100644
index f896db767..000000000
--- a/My Settings	
+++ /dev/null
@@ -1,27 +0,0 @@
-# generated by Slic3r Prusa Edition 1.38.4 on 2017-12-20 at 11:02:03
-bed_temperature = 0
-bridge_fan_speed = 100
-compatible_printers = 
-compatible_printers_condition = 
-cooling = 1
-disable_fan_first_layers = 3
-end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n"
-extrusion_multiplier = 1
-fan_always_on = 0
-fan_below_layer_time = 60
-filament_colour = #29b2b2
-filament_cost = 0
-filament_density = 0
-filament_diameter = 3
-filament_max_volumetric_speed = 0
-filament_notes = ""
-filament_soluble = 0
-filament_type = PLA
-first_layer_bed_temperature = 0
-first_layer_temperature = 205
-max_fan_speed = 100
-min_fan_speed = 35
-min_print_speed = 10
-slowdown_below_layer_time = 5
-start_filament_gcode = "; Filament gcode\n"
-temperature = 200

From df328179929b977398f47ee4d8d2a42958d37839 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 9 Apr 2018 14:57:23 +0200
Subject: [PATCH 087/118] Auto assign multi-part extruders

---
 xs/src/libslic3r/Model.cpp | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index ad2ce54cd..d49594c72 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -406,9 +406,24 @@ void Model::convert_multipart_object()
     ModelObject* object = new ModelObject(this);
     object->input_file = this->objects.front()->input_file;
     
+    unsigned int auto_extruder_id = 1;
+    char str_extruder[64];
+
     for (const ModelObject* o : this->objects)
         for (const ModelVolume* v : o->volumes)
-            object->add_volume(*v)->name = o->name;
+        {
+            ModelVolume* new_v = object->add_volume(*v);
+            if (new_v != nullptr)
+            {
+                new_v->name = o->name;
+
+                sprintf(str_extruder, "%ud", auto_extruder_id);
+                new_v->config.set_deserialize("extruder", str_extruder);
+
+                if (++auto_extruder_id > 4)
+                    auto_extruder_id = 1;
+            }
+        }
 
     for (const ModelInstance* i : this->objects.front()->instances)
         object->add_instance(*i);

From 674ddcd73a86b93f393ff50856a5111c81438123 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 9 Apr 2018 16:50:17 +0200
Subject: [PATCH 088/118] Show preset_tree according to preset inheritance

---
 xs/src/slic3r/GUI/Preset.cpp       | 10 +++++
 xs/src/slic3r/GUI/Preset.hpp       |  4 +-
 xs/src/slic3r/GUI/Tab.cpp          | 64 ++++++++++++++++++++++++------
 xs/src/slic3r/GUI/wxExtensions.cpp |  2 +-
 4 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index d612896a5..845f11c96 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -417,6 +417,16 @@ const Preset* PresetCollection::get_selected_preset_parent() const
     return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset;
 }
 
+const Preset* PresetCollection::get_preset_parent(const Preset& child) const
+{
+    auto *inherits = dynamic_cast<const ConfigOptionString*>(child.config.option("inherits"));
+    if (inherits == nullptr || inherits->value.empty())
+// 		return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr; 
+		return nullptr; 
+    const Preset* preset = this->find_preset(inherits->value, false);
+    return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset;
+}
+
 const std::string& PresetCollection::get_suffix_modified() {
 	return g_suffix_modified;
 }
diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp
index f19f511c3..c038160f4 100644
--- a/xs/src/slic3r/GUI/Preset.hpp
+++ b/xs/src/slic3r/GUI/Preset.hpp
@@ -209,7 +209,9 @@ public:
     // The parent preset may be a system preset or a user preset, which will be
     // reflected by the UI.
     const Preset*   get_selected_preset_parent() const;
-    // Return the selected preset including the user modifications.
+	// get parent preset for some child preset
+	const Preset*	get_preset_parent(const Preset& child) const;
+	// Return the selected preset including the user modifications.
     Preset&         get_edited_preset()         { return m_edited_preset; }
     const Preset&   get_edited_preset() const   { return m_edited_preset; }
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 5f727830f..fa414563c 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -39,7 +39,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 	// preset chooser
 	m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(270, -1), 0, 0,wxCB_READONLY);
 
-	m_cc_presets_choice = new wxComboCtrl(panel, wxID_ANY, L("Presets"), wxDefaultPosition, wxSize(270, -1), wxCB_READONLY);
+	m_cc_presets_choice = new wxComboCtrl(panel, wxID_ANY, L(""), wxDefaultPosition, wxDefaultSize/*wxSize(270, -1)*/, wxCB_READONLY);
 	wxDataViewTreeCtrlComboPopup* popup = new wxDataViewTreeCtrlComboPopup;
 	if (popup != nullptr)
 	{
@@ -63,8 +63,8 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 //			popup->OnDataViewTreeCtrlSelection(evt);
 		});
 
-		popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
-		popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
+// 		popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
+// 		popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
 
 		auto icons = new wxImageList(16, 16, true, 1);
 		popup->SetImageList(icons);
@@ -307,7 +307,7 @@ void Tab::update_changed_ui()
 			is_nonsys_value = true;
 			sys_icon = m_nonsys_btn_icon;
 			if(find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end())
-				color = wxSYS_COLOUR_WINDOWTEXT;
+				color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
 			else
 				color = *get_modified_label_clr();
 		}
@@ -380,7 +380,7 @@ void Tab::update_sys_ui_after_sel_preset()
 			field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG));
 			field->m_is_nonsys_value = true;
 			if (field->m_Label != nullptr){
-				field->m_Label->SetForegroundColour(wxSYS_COLOUR_WINDOWTEXT);
+				field->m_Label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
 				field->m_Label->Refresh(true);
 			}
 		}
@@ -427,7 +427,7 @@ void Tab::update_changed_tree_ui()
 			else if (modified_page)
 				m_treectrl->SetItemTextColour(cur_item, *get_modified_label_clr());
 			else
-				m_treectrl->SetItemTextColour(cur_item, wxSYS_COLOUR_WINDOWTEXT);
+				m_treectrl->SetItemTextColour(cur_item, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
 
 			page->m_is_nonsys_values = !sys_page;
 			page->m_is_modified_values = modified_page;
@@ -2150,11 +2150,16 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 	if (popup != nullptr)
 	{
 		popup->DeleteAllItems();
-		auto root_1 = popup->AppendContainer(wxDataViewItem(0), _(L("Root 1")));
+
+		auto root_exp1 = popup->AppendContainer(wxDataViewItem(0), "Exp_root_1");
+		auto item1 = popup->AppendItem(root_exp1, "child_1_1");
+		auto item2 = popup->AppendItem(root_exp1, "child_1_2");
+		auto item3 = popup->AppendItem(item2, "child_2_1");
+
 		auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets")));
+		auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
+
 		auto show_def = get_app_config()->get("no_defaults")[0] != '1';
-		wxDataViewItem root_def;
-		if (show_def) root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
 
 		for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) {
 			const Preset &preset = presets[i];
@@ -2170,9 +2175,41 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 			else if (show_def && preset.is_default)
 				item = popup->AppendItem(root_def, preset_name, 
 										 preset.is_compatible ? icon_compatible : icon_incompatible);
-			else {
-				item = popup->AppendItem(root_1, preset_name, 
-										 preset.is_compatible ? icon_compatible : icon_incompatible);				
+			else 
+			{
+				auto parent = m_presets->get_preset_parent(preset);
+				if (parent == nullptr)
+					item = popup->AppendItem(root_def, preset_name,
+											 preset.is_compatible ? icon_compatible : icon_incompatible);
+				else
+				{
+					auto parent_name = parent->name;
+
+					wxDataViewTreeStoreContainerNode *node = popup->GetStore()->FindContainerNode(root_sys);
+					if (node) 
+					{
+						wxDataViewTreeStoreNodeList::iterator iter;
+						for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++)
+						{
+							wxDataViewTreeStoreNode* child = *iter;
+							auto child_item = child->GetItem();
+							auto item_text = popup->GetItemText(child_item);
+							if (item_text == parent_name)
+							{
+								auto added_child = popup->AppendItem(child->GetItem(), preset_name,
+									preset.is_compatible ? icon_compatible : icon_incompatible);
+								if (!added_child){
+									popup->DeleteItem(child->GetItem());
+									auto new_parent = popup->AppendContainer(root_sys, parent_name,
+										preset.is_compatible ? icon_compatible : icon_incompatible);
+									popup->AppendItem(new_parent, preset_name,
+										preset.is_compatible ? icon_compatible : icon_incompatible);
+								}
+								break;
+							}
+						}
+					}
+				}
 			}
 
 			cnt_items++;
@@ -2181,8 +2218,9 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 				m_cc_presets_choice->SetText(preset_name);
 			}
 		}
+		if (popup->GetStore()->GetChildCount(root_def) == 0)
+			popup->DeleteItem(root_def);
 	}
-	popup->SetItemsCnt(cnt_items+2);
 	ui->Thaw();
 }
 
diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp
index 666da7f8a..8bc282474 100644
--- a/xs/src/slic3r/GUI/wxExtensions.cpp
+++ b/xs/src/slic3r/GUI/wxExtensions.cpp
@@ -119,7 +119,7 @@ const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22;
 
 bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent)
 {
-	return wxDataViewTreeCtrl::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0), wxSize(270, -1));
+	return wxDataViewTreeCtrl::Create(parent, wxID_ANY/*HIGHEST + 1*/, wxPoint(0, 0), wxDefaultSize/*wxSize(270, -1)*/, wxDV_NO_HEADER);
 }
 /*
 wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)

From 0cca7bb5e736c89088cc84cebb3702efbc676a1a Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 9 Apr 2018 17:09:00 +0200
Subject: [PATCH 089/118] Dont use select_preset from preset_tree...

---
 xs/src/slic3r/GUI/Tab.cpp | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index fa414563c..a40da002b 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -58,9 +58,8 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 			{
 				m_cc_presets_choice->SetText(selected);
 				std::string selected_string = selected.ToUTF8().data();
-				select_preset(selected_string);
+// 				select_preset(selected_string);
 			}				
-//			popup->OnDataViewTreeCtrlSelection(evt);
 		});
 
 // 		popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
@@ -2151,11 +2150,6 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 	{
 		popup->DeleteAllItems();
 
-		auto root_exp1 = popup->AppendContainer(wxDataViewItem(0), "Exp_root_1");
-		auto item1 = popup->AppendItem(root_exp1, "child_1_1");
-		auto item2 = popup->AppendItem(root_exp1, "child_1_2");
-		auto item3 = popup->AppendItem(item2, "child_2_1");
-
 		auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets")));
 		auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
 

From fa4462514bb3b5a9e79e6819d007112169c6e787 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 10 Apr 2018 10:31:58 +0200
Subject: [PATCH 090/118] Added preset grouping to
 PresetCollection::update_tab_ui

---
 xs/src/slic3r/GUI/2DBed.hpp  |  6 ++++--
 xs/src/slic3r/GUI/Preset.cpp | 36 ++++++++++++++++++++++++++++++++----
 2 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/xs/src/slic3r/GUI/2DBed.hpp b/xs/src/slic3r/GUI/2DBed.hpp
index 859417efb..a170b708a 100644
--- a/xs/src/slic3r/GUI/2DBed.hpp
+++ b/xs/src/slic3r/GUI/2DBed.hpp
@@ -6,7 +6,7 @@ namespace GUI {
 
 class Bed_2D : public wxPanel
 {
-	bool		m_user_drawn_background = false;
+	bool		m_user_drawn_background = true;
 
 	bool		m_painted = false;
 	bool		m_interactive = false;
@@ -26,7 +26,9 @@ public:
 	{
 		Create(parent, wxID_ANY, wxDefaultPosition, wxSize(250, -1), wxTAB_TRAVERSAL);
 //		m_user_drawn_background = $^O ne 'darwin';
-		m_user_drawn_background = true;
+#ifdef __APPLE__
+		m_user_drawn_background = false;
+#endif /*__APPLE__*/
 		Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); }));
 //		EVT_ERASE_BACKGROUND($self, sub{}) if $self->{user_drawn_background};
 //		Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent  event){/*mouse_event()*/; }));
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 845f11c96..bc34e193a 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -531,16 +531,44 @@ void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatibl
         return;
     ui->Freeze();
     ui->Clear();
+	std::map<wxString, bool> nonsys_presets;
+	wxString selected = "";
     for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) {
         const Preset &preset = this->m_presets[i];
         if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
             continue;
         const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible;
-        ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
-            (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
-		if (i == m_idx_selected)
-            ui->SetSelection(ui->GetCount() - 1);
+//         ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
+//             (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
+// 		if (i == m_idx_selected)
+//             ui->SetSelection(ui->GetCount() - 1);
+
+		if (preset.is_default || preset.is_system){
+			ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
+				(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
+			if (i == m_idx_selected)
+				ui->SetSelection(ui->GetCount() - 1);
+		}
+		else
+		{
+			nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible);
+			if (i == m_idx_selected)
+				selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
+		}
+		if (preset.is_default)
+			ui->Append("______________________", wxNullBitmap);
     }
+	if (!nonsys_presets.empty())
+	{
+		ui->Append("______________________", wxNullBitmap);
+		for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
+			const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible;
+			ui->Append(it->first,
+				(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
+			if (it->first == selected)
+				ui->SetSelection(ui->GetCount() - 1);
+		}
+	}
     ui->Thaw();
 }
 

From f49a7be90c93fbfa7c9d81f1560dea54b8116186 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 10 Apr 2018 10:50:18 +0200
Subject: [PATCH 091/118] An attempt to get RammingChart background colour
 consistent with its parent(s) on all platforms

---
 xs/src/slic3r/GUI/RammingChart.cpp    | 5 +++++
 xs/src/slic3r/GUI/WipeTowerDialog.cpp | 8 ++++++++
 2 files changed, 13 insertions(+)

diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
index ca76298af..1ef43be02 100644
--- a/xs/src/slic3r/GUI/RammingChart.cpp
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -15,6 +15,11 @@ wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
 
 void Chart::draw() {
     wxAutoBufferedPaintDC dc(this); // unbuffered DC caused flickering on win
+
+    dc.SetBrush(GetBackgroundColour());
+    dc.SetPen(GetBackgroundColour());
+    dc.DrawRectangle(GetClientRect());  // otherwise the background would end up black on windows
+
     dc.SetPen(*wxBLACK_PEN);
     dc.SetBrush(*wxWHITE_BRUSH);
     dc.DrawRectangle(m_rect);
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
index 421ae272d..b34de0745 100644
--- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -14,6 +14,13 @@ RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
 : wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
 {
     m_panel_ramming  = new RammingPanel(this,parameters);
+
+    // Not found another way of getting the background colours of RammingDialog, RammingPanel and Chart correct than setting
+    // them all explicitely. Reading the parent colour yielded colour that didn't really match it, no wxSYS_COLOUR_... matched
+    // colour used for the dialog. Same issue (and "solution") here : https://forums.wxwidgets.org/viewtopic.php?f=1&t=39608
+    // Whoever can fix this, feel free to do so.
+    this->           SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
+    m_panel_ramming->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
     m_panel_ramming->Show(true);
     this->Show();
 
@@ -63,6 +70,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
 		buttons.push_back(std::make_pair(x, y));
 
 	m_chart = new Chart(this, wxRect(10, 10, 480, 360), buttons, ramming_speed_size, 0.25f);
+    m_chart->SetBackgroundColour(parent->GetBackgroundColour()); // see comment in RammingDialog constructor
  	sizer_chart->Add(m_chart, 0, wxALL, 5);
 
     m_widget_time						= new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,0.,5.0,3.,0.5);        

From a541f5dfe1d3b7b0dc51e38d09b3883a3a14aa12 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 10 Apr 2018 11:43:04 +0200
Subject: [PATCH 092/118] Fix of the fix

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

diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 9109e8b5c..9450f44ae 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -545,7 +545,7 @@ unsigned get_colour_approx_luma(const wxColour &colour)
 	double g = colour.Green();
 	double b = colour.Blue();
 
-	std::round(std::sqrt(
+	return std::round(std::sqrt(
 		r * r * .241 +
 		g * g * .691 +
 		b * b * .068

From 9993f2215d56d7dfdf727794b590756893a2ba39 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 10 Apr 2018 12:17:55 +0200
Subject: [PATCH 093/118] Auto assignement of extruder, after object's
 splitting to parts

---
 xs/src/libslic3r/Model.cpp | 40 +++++++++++++++++++++++++++++---------
 xs/src/libslic3r/Model.hpp |  6 ++++++
 2 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index d49594c72..561106713 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -16,6 +16,8 @@
 
 namespace Slic3r {
 
+    unsigned int Model::s_auto_extruder_id = 1;
+
 Model::Model(const Model &other)
 {
     // copy materials
@@ -405,9 +407,8 @@ void Model::convert_multipart_object()
     
     ModelObject* object = new ModelObject(this);
     object->input_file = this->objects.front()->input_file;
-    
-    unsigned int auto_extruder_id = 1;
-    char str_extruder[64];
+
+    reset_auto_extruder_id();
 
     for (const ModelObject* o : this->objects)
         for (const ModelVolume* v : o->volumes)
@@ -416,12 +417,7 @@ void Model::convert_multipart_object()
             if (new_v != nullptr)
             {
                 new_v->name = o->name;
-
-                sprintf(str_extruder, "%ud", auto_extruder_id);
-                new_v->config.set_deserialize("extruder", str_extruder);
-
-                if (++auto_extruder_id > 4)
-                    auto_extruder_id = 1;
+                new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string());
             }
         }
 
@@ -481,6 +477,28 @@ bool Model::fits_print_volume(const FullPrintConfig &config) const
     return print_volume.contains(transformed_bounding_box());
 }
 
+unsigned int Model::get_auto_extruder_id()
+{
+    unsigned int id = s_auto_extruder_id;
+
+    if (++s_auto_extruder_id > 4)
+        reset_auto_extruder_id();
+
+    return id;
+}
+
+std::string Model::get_auto_extruder_id_as_string()
+{
+    char str_extruder[64];
+    sprintf(str_extruder, "%ud", get_auto_extruder_id());
+    return str_extruder;
+}
+
+void Model::reset_auto_extruder_id()
+{
+    s_auto_extruder_id = 1;
+}
+
 ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) :  
     name(other.name),
     input_file(other.input_file),
@@ -1010,6 +1028,9 @@ size_t ModelVolume::split()
     size_t idx = 0;
     size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
     std::string name = this->name;
+
+    Model::reset_auto_extruder_id();
+
     for (TriangleMesh *mesh : meshptrs) {
         mesh->repair();
         if (idx == 0)
@@ -1019,6 +1040,7 @@ size_t ModelVolume::split()
         char str_idx[64];
         sprintf(str_idx, "_%d", idx + 1);
         this->object->volumes[ivolume]->name = name + str_idx;
+        this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string());
         delete mesh;
         ++ idx;
     }
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index bf7f60e36..0c0ffe776 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -230,6 +230,8 @@ private:
 // all objects may share mutliple materials.
 class Model
 {
+    static unsigned int s_auto_extruder_id;
+
 public:
     // Materials are owned by a model and referenced by objects through t_model_material_id.
     // Single material may be shared by multiple models.
@@ -288,6 +290,10 @@ public:
     bool fits_print_volume(const FullPrintConfig &config) const;
 
     void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
+
+    static unsigned int get_auto_extruder_id();
+    static std::string get_auto_extruder_id_as_string();
+    static void reset_auto_extruder_id();
 };
 
 }

From a81f78045b209420cbc243af27890bd196cc2697 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 10 Apr 2018 12:47:09 +0200
Subject: [PATCH 094/118] Delete key enabled in Object's Settings Dialog also
 for 3D view

---
 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 47fd8a092..f7e38ed87 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -9,7 +9,7 @@ use utf8;
 use File::Basename qw(basename);
 use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL
     wxTheApp);
-use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN);
+use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN);
 use base 'Wx::Panel';
 
 use constant ICON_OBJECT        => 0;
@@ -191,6 +191,14 @@ sub new {
     EVT_BUTTON($self, $self->{btn_split}, \&on_btn_split);
     EVT_BUTTON($self, $self->{btn_move_up}, \&on_btn_move_up);
     EVT_BUTTON($self, $self->{btn_move_down}, \&on_btn_move_down);
+    EVT_KEY_DOWN($canvas, sub {
+        my ($canvas, $event) = @_;
+        if ($event->GetKeyCode == WXK_DELETE) {
+            $canvas->GetParent->on_btn_delete;
+        } else {
+            $event->Skip;
+        }
+    });
     
     $self->reload_tree;
     

From b961711f28c90886b32ce88a2130113dac41f455 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 10 Apr 2018 13:39:10 +0200
Subject: [PATCH 095/118] Fixed autoscale of objects after loading

---
 lib/Slic3r/GUI/Plater.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index ecac430a4..c74b2d32c 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -737,7 +737,7 @@ sub load_model_objects {
         {
             # if the object is too large (more than 5 times the bed), scale it down
             my $size = $o->bounding_box->size;
-            my $ratio = max(@$size[X,Y]) / unscale(max(@$bed_size[X,Y]));
+            my $ratio = max($size->x / unscale($bed_size->x), $size->y / unscale($bed_size->y));
             if ($ratio > 5) {
                 $_->set_scaling_factor(1/$ratio) for @{$o->instances};
                 $scaled_down = 1;

From c1d25ba25926ef6aeb46edc6984926f1ff68fae2 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 10 Apr 2018 14:00:48 +0200
Subject: [PATCH 096/118] Experiments with wxDataViewTreeCtrl

---
 xs/src/slic3r/GUI/Tab.cpp | 108 ++++++++++++++++++++++++++++++++++++++
 xs/src/slic3r/GUI/Tab.hpp |   3 ++
 2 files changed, 111 insertions(+)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index a40da002b..c074a1e58 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -123,6 +123,32 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 	m_hsizer = new wxBoxSizer(wxHORIZONTAL);
 	sizer->Add(m_hsizer, 1, wxEXPAND, 0);
 
+
+
+	//temporary left vertical sizer
+	m_left_sizer = new wxBoxSizer(wxVERTICAL);
+	m_hsizer->Add(m_left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3);
+
+	// tree
+	m_presetctrl = new wxDataViewTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(200, -1), wxDV_NO_HEADER);
+	m_left_sizer->Add(m_presetctrl, 1, wxEXPAND);
+	m_preset_icons = new wxImageList(16, 16, true, 1);
+	m_presetctrl->SetImageList(m_preset_icons);
+	m_preset_icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG));
+	m_preset_icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG));
+
+	m_presetctrl->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxCommandEvent& evt)
+	{
+		auto selected = m_presetctrl->GetItemText(m_presetctrl->GetSelection());
+		if (selected != _(L("System presets")) && selected != _(L("Default presets")))
+		{
+			std::string selected_string = selected.ToUTF8().data();
+			select_preset(selected_string);
+		}
+	});
+
+
+
 	//left vertical sizer
 	m_left_sizer = new wxBoxSizer(wxVERTICAL);
 	m_hsizer->Add(m_left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3);
@@ -521,6 +547,7 @@ void Tab::update_tab_ui()
 {
 	m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets);
 	update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets);
+	update_presetsctrl(m_presetctrl, m_show_incompatible_presets);
 }
 
 // Load a provied DynamicConfig into the tab, modifying the active preset.
@@ -2132,6 +2159,87 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
 	return sizer; 
 }
 
+void Tab::update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible)
+{
+	if (ui == nullptr)
+		return;
+	ui->Freeze();
+	ui->DeleteAllItems();
+	auto presets = m_presets->get_presets();
+	auto idx_selected = m_presets->get_idx_selected();
+	auto suffix_modified = m_presets->get_suffix_modified();
+	int icon_compatible = 0;
+	int icon_incompatible = 1;
+	int cnt_items = 0;
+
+	auto root_sys = ui->AppendContainer(wxDataViewItem(0), _(L("System presets")));
+	auto root_def = ui->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
+
+	auto show_def = get_app_config()->get("no_defaults")[0] != '1';
+
+	for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) {
+		const Preset &preset = presets[i];
+		if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected))
+			continue;
+
+		auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str());
+
+		wxDataViewItem item;
+		if (preset.is_system)
+			item = ui->AppendItem(root_sys, preset_name,
+			preset.is_compatible ? icon_compatible : icon_incompatible);
+		else if (show_def && preset.is_default)
+			item = ui->AppendItem(root_def, preset_name,
+			preset.is_compatible ? icon_compatible : icon_incompatible);
+		else
+		{
+			auto parent = m_presets->get_preset_parent(preset);
+			if (parent == nullptr)
+				item = ui->AppendItem(root_def, preset_name,
+				preset.is_compatible ? icon_compatible : icon_incompatible);
+			else
+			{
+				auto parent_name = parent->name;
+
+				wxDataViewTreeStoreContainerNode *node = ui->GetStore()->FindContainerNode(root_sys);
+				if (node)
+				{
+					wxDataViewTreeStoreNodeList::iterator iter;
+					for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++)
+					{
+						wxDataViewTreeStoreNode* child = *iter;
+						auto child_item = child->GetItem();
+						auto item_text = ui->GetItemText(child_item);
+						if (item_text == parent_name)
+						{
+							auto added_child = ui->AppendItem(child->GetItem(), preset_name,
+								preset.is_compatible ? icon_compatible : icon_incompatible);
+							if (!added_child){
+								ui->DeleteItem(child->GetItem());
+								auto new_parent = ui->AppendContainer(root_sys, parent_name,
+									preset.is_compatible ? icon_compatible : icon_incompatible);
+								ui->AppendItem(new_parent, preset_name,
+									preset.is_compatible ? icon_compatible : icon_incompatible);
+							}
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		cnt_items++;
+		if (i == idx_selected){
+			ui->Select(item);
+			m_cc_presets_choice->SetText(preset_name);
+		}
+	}
+	if (ui->GetStore()->GetChildCount(root_def) == 0)
+		ui->DeleteItem(root_def);
+
+	ui->Thaw();
+}
+
 void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
 {
 	if (ui == nullptr)
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 5e2eec80d..8a2c38e05 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -97,6 +97,8 @@ protected:
 	wxButton*			m_undo_btn;
 	wxButton*			m_undo_to_sys_btn;
 	wxComboCtrl*		m_cc_presets_choice;
+	wxDataViewTreeCtrl*	m_presetctrl;
+	wxImageList*		m_preset_icons;
 
 	int					m_icon_count;
 	std::map<std::string, size_t>	m_icon_index;		// Map from an icon file name to its index
@@ -149,6 +151,7 @@ public:
 	bool		may_discard_current_dirty_preset(PresetCollection* presets = nullptr, std::string new_printer_name = "");
 	wxSizer*	compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
 
+	void		update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible);
 	void		load_key_value(std::string opt_key, boost::any value);
 	void		reload_compatible_printers_widget();
 

From d789b5c0df5042516c66729fa9f63d63907d9180 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 10 Apr 2018 14:12:16 +0200
Subject: [PATCH 097/118] Fixed warning texture not disappearing when deleting
 object out of bed

---
 lib/Slic3r/GUI/Plater/3D.pm | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index 36d55d27b..e9416b3cc 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -231,6 +231,9 @@ sub reload_scene {
             Slic3r::GUI::_3DScene::reset_warning_texture();
             $self->on_enable_action_buttons->(1) if ($self->on_enable_action_buttons);
         }
+    } else {
+        $self->set_warning_enabled(0);
+        Slic3r::GUI::_3DScene::reset_warning_texture();
     }
 }
 

From e7520c3d18709337e5340dff6a2d0f995cbdc27c Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 10 Apr 2018 14:52:03 +0200
Subject: [PATCH 098/118] Don't use select_preset on OS X

---
 xs/src/slic3r/GUI/Tab.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index c074a1e58..1b70dfa5c 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -58,7 +58,10 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 			{
 				m_cc_presets_choice->SetText(selected);
 				std::string selected_string = selected.ToUTF8().data();
-// 				select_preset(selected_string);
+#ifdef __APPLE__
+#else
+ 				select_preset(selected_string);
+#endif
 			}				
 		});
 
@@ -143,7 +146,10 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 		if (selected != _(L("System presets")) && selected != _(L("Default presets")))
 		{
 			std::string selected_string = selected.ToUTF8().data();
+#ifdef __APPLE__
+#else
 			select_preset(selected_string);
+#endif
 		}
 	});
 

From 762306d98536522bed52d493c92684cdea7be2ea Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 11 Apr 2018 13:56:37 +0200
Subject: [PATCH 099/118] Added preset grouping to all presets ComboBoxes

Changed icons for action_undo, sys_lock and sys_unlock.
There are used same icons for all OS now.

Deleted/Commented temporarily used wxDataViewTreeCtrl
---
 resources/icons/action_undo.png    | Bin 616 -> 491 bytes
 resources/icons/sys_lock.png       | Bin 448 -> 510 bytes
 resources/icons/sys_unlock.png     | Bin 454 -> 419 bytes
 xs/src/slic3r/GUI/BitmapCache.hpp  |   5 +++-
 xs/src/slic3r/GUI/Preset.cpp       |  42 ++++++++++++++++++++++++-----
 xs/src/slic3r/GUI/PresetBundle.cpp |  34 ++++++++++++++++++++---
 xs/src/slic3r/GUI/Tab.cpp          |  33 ++++++++++++-----------
 7 files changed, 87 insertions(+), 27 deletions(-)

diff --git a/resources/icons/action_undo.png b/resources/icons/action_undo.png
index 866ae9773bf144069986bf74e5524fe7b387ff48..877f159868640a0399143631438313a31dacf1a1 100644
GIT binary patch
delta 429
zcmV;e0aE_x1nUEkNPiB)000id0mpBsWB>pGfJsC_R5(wykxNSgQ545V%^63>_gk}<
zP(iE4M^Mt2kto`yMY*Xq(ym%bkF_lfw55$L>|?08G8A_%g@5;6hEPTeiGFZ6bB25V
z=f(XiNjV1fb9Oe?)@kzW0w1lG;<VZ@9|$7g_9AzBro2=t3V(ncwc&8cOnpAiTdkHN
z4$F}RL>HHs`CM+;%oc=veze<dCNx!ml0C|#1j8Ilra}v$FdUMVc_XJsuh(OpsLv<!
zTq>oEIz4z|G=8mCVKs?tti+~F@_!Maw3<!&=zEEGg#x?|Cunz{osL!@8H+<QiHaIB
zy4~(rED0j$f`2RJvi3S#@dUh17xvQWv7}KX!Ndf}RkQ+!`v>s5J#gC{6K{WeeZvay
zIs^LszIKFWqX7e%nJr3|$3&irMOaAnus?v^?VX7Q7{{3m%)Ed|FvR@oN)#TlIG4@N
z{)t8&gTY|B0Ocy5C->H|QRvob+jMp+Mq2+ld6dWd2PWktn^i8)FO>Ised=QW2*-T^
X&Lg-nvC@Jh00000NkvXXu0mjfw4BNi

delta 555
zcmV+`0@VHM1Ly>hNPiM^000tn0p4aGcmMza{YgYYR5(v{Q_oKmQ4pTpZM(a)6xy=1
zpa=*76HgY@geDgFBQ!?iRm0uNLA?`hz=@cMM?7G{j~fR64N0i<QcTkxnl$O9?dO|m
z-=+)rCA0J9$D5h=y|+fY-G%}&48tJ3HceCPPN(DhG;|(ovwuIR*Op~T@ae*$6uJ(b
zC%V;Yp<gr}&OhL+o(1rv*K=6{<+*d|@{*GAA*$Ewlu3E?`Qs-#IX;$2IDP}V664p|
znRK0=xQCg^DUs1+3h{v`#z)gAl}eKL-wE=|%k<%Wfy(8w*i4|~IMO{73eoK3G*zqB
z;3T?xrBV^i_<wP5fUsdAqJ;7D^b~`Ri@TXIJbsiFpPs-k@bdW!tmgBwX8b7@10;Ug
z-Gw)tfNh2F_v%V~fdWq!77<fj-n@D(HiM^9$B;GOLd<rN7>wiS@KAib*XMxm-F02E
z9b0U*fjrL>9vEHSy?@U2p(j{-`wr>U2vS4CVlz0_dw;3e+1iGeNGe_|)M_>H1&&a>
zxPaoeOH?+CX0s`N#`pbwL~RFm(iuEk%88Hn`2}>5^+Evy5gQX@<D%#8&tOCmryiF0
z+;9YqMniIM6wpOBH#QI^Lyp3%^Vl)fos0F4uEpR_j}8Gp5|u;WzV6Za`EQCwW3;lm
tLQA=v#3Y&AeguAJjoaS*?Z34_{{UR92b{cyd5r)7002ovPDHLkV1g?@8MXib

diff --git a/resources/icons/sys_lock.png b/resources/icons/sys_lock.png
index 0519c44a17c8f8f30b5f0a5b558e4bd71dfc13d6..a33eadbcabbcfe135b0da8841aabd6a564963992 100644
GIT binary patch
delta 448
zcmV;x0YCo01O5Y$NPiB)000id0mpBsWB>pGlSxEDR5(wKl08VnP!xsz0UbmT5k$eE
zgXrMqrr6C@aB+3FlauHof*`mFD!6vYP$*5)rcK(WO;SW06-5wdad1!vrD~1eGf!#L
zR&wBy_TGEWJ@3BP)d{Mq4i<~WwMwPp+O~b?IL>9MR5~^cV}F`*x8KzPiA16g@-C!T
z$Pb8|^S$AJjo+uHX+~sgd(bc|iPxD-W+9bI4KaB_I6&l`?>6X>?X938=DO}DE;c>S
z`?HJK48r#a+v12a-3kQu5UAl~S*CuVY1#<n3q-Ec>GY^fHzSwJ^=Gr$6)f-Tx;~#w
zCdYmQQiS?OK7XIzQWRyJ_U2_m8n!I!P)ra@`!CAnvLyycs*ggUu!QxuYPITx3682r
zst?Y*ia<TkSSR-+b`r>YxG*KvM>_$Q=CG3}H?k8H2_=Bd10Gf{wJ2}kxv>`3!7cYc
ziO><Xuf|#^0j553ggI_UYs`xXB|v=8cqW7bdEw5GR5>5KgGKK6CD_<5#Bp$SBB?&&
q@%RASUT2~%>~#t;S&&s#o%#mod=~CNPr6V50000<MNUMnLSTY1qSUGY

delta 385
zcmV-{0e=4e1Hc22NPiM^000tn0p4aGcmMzaRY^oaR5(wClh4Y6KorG~mPLrWqJ&lv
zfltuVMbF(U*hXj(xd@af2&0hD{(RrLIh3aLgVCAmoilUKjN0QL)Uey_u-R;IKA&M2
z270|7CX-42^+~P3dc8&v1kf}MmSy2`xgg6j5prEO&#Do)-G6SF&*x~j+lA!ia)~eu
zF&d4e5nY}ow%e_&JRXnp$P>qL!1Y*~qEaA95?rrWn5LOWUNoCcoKB~FR8s;4j>jWx
z+s-2|_WQk*I2a6M{2}1`z9cmo4M{BPGp^p#>9i<tI2_=4o|yLwig&x+q6e&-$}~_l
z>ZfTc6W-&xu6pBszgL^6U_z(U!E82@6X617ehT#aed!N}L)k@{uL3M*wOUD^=JE*3
zd=*e0kn(9L3IbJ0tyT+*#R730i*fHTzYw8e{9#zTYJ``H$!sc4Ln;1ok)kM)i^T2M
f!6}^VI1c^*)N{#<rV0rk00000NkvXXu0mjfAn&$O

diff --git a/resources/icons/sys_unlock.png b/resources/icons/sys_unlock.png
index 189bc24e785d3faf866b9335ba721e203a99d841..d53c288a1c2fa3e2d6c99324fde52393102da8fb 100644
GIT binary patch
delta 356
zcmV-q0h|8D1ET|wNPiB)000id0mpBsWB>pGI7vi7R5(wilf6pBKoEfQPa;7{<ceS*
zmDmcwN+kFImcdRe#KO`>tRlXFjo5pYh4=*4cD{iK7FLShT_K!z=QonTo?dXlfsZ7!
zGv7`&#HgyuD$CMV$@AO|f?&yUoORo_H$Bf=$+FD<Stq@Q+<z2B;WWf?Ja1XnfoYoe
z@D2lbgfrjwGqqNFrv$PK%|~@pAMW55o?!@GgJ>1FuDeRn6TBeW55uq%MbQGUvO(@#
znk8BV_2?tGOp;`-$qO=_`Yiks(0B?HM8|4j8?yu_@PXh^Eo|dkFeA+Rsu#@!?7GA5
zukfZpH%-%E>RlBO?6DvRI8l)AzXhL4PPo<)49P9al-%zsd2PWM2DPB4<h2DOdT&HP
zTq${N!I;Jr7GX`vw+gmdcmO%Lzqn}rP8shXVHjUm-K#+eM`YOm0000<MNUMnLSTY(
C*QJX9

delta 392
zcmV;30eAkR1I7c8NPiM^000tn0p4aGcmMzaTS-JgR5(wKld+D2Fbswr7(gtQI#n#4
z7#O;giC3u9y$kQe!o&mc63je6*()JYF?B;iP$(C>B$xE=di^BJPU8GEvCF1unmK?l
z#sFi}G-=&54YX|wp6AgT&mND5%tH_3IL@jeS(c%qW>FN;8Gjeh@-+b@j$?Vrb=^4#
z0=ae^hjOs$9|8iAoagznGt>Egzn_bukZWO@K$0YM`E)wbT0IkpJz)mFAgSw`d>Dr0
z1-xWwuV@1I`#`?yI`Y_!zf8cW48x$doX=;tTrS}IK5RA{{JbUY8*HO(+pt=#o)w`0
zlBTJA!2fKw+kep=?G1Q~$m4L(fN-!f-b#wq{q=fH9?{<5m9i}1dc9K2e~>&Pgpjua
z{Ij5{DvH%hs@PkB<M9aF?Uv$?E5JmNd^jAyvMh=bvHxoYqW3C&-@`CGXG7(7yDbr~
m2LBkSs1C;`;0!Rv;0we*U~e+nw^aZD002ovPDHLkU;%;)@4GGl

diff --git a/xs/src/slic3r/GUI/BitmapCache.hpp b/xs/src/slic3r/GUI/BitmapCache.hpp
index 0cf9d8acf..46c7db3a1 100644
--- a/xs/src/slic3r/GUI/BitmapCache.hpp
+++ b/xs/src/slic3r/GUI/BitmapCache.hpp
@@ -31,7 +31,10 @@ public:
 
 	static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
 	static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }
-	static wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); }
+	static wxBitmap mkclear(size_t width, size_t height) {
+		wxColour bg_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+		return mksolid(width, height, bg_clr.Red(), bg_clr.Green(), bg_clr.Blue()/*, 0, 0, 0*/,  wxALPHA_TRANSPARENT);
+	}
 
 private:
     std::map<std::string, wxBitmap*>	m_map;
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index bc34e193a..0afbcf8cd 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -512,16 +512,44 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
     // Otherwise fill in the list from scratch.
     ui->Freeze();
     ui->Clear();
+	std::map<wxString, bool> nonsys_presets;
+	wxString selected = "";
     for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) {
         const Preset &preset = this->m_presets[i];
         if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
             continue;
         const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible;
-        ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
-            (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
-		if (i == m_idx_selected)
-            ui->SetSelection(ui->GetCount() - 1);
-    }
+//         ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
+//             (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
+// 		if (i == m_idx_selected)
+//             ui->SetSelection(ui->GetCount() - 1);
+
+		if (preset.is_default || preset.is_system){
+			ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
+				(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
+			if (i == m_idx_selected)
+				ui->SetSelection(ui->GetCount() - 1);
+		}
+		else
+		{
+			nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible);
+			if (i == m_idx_selected)
+				selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
+		}
+		if (preset.is_default)
+			ui->Append("------------------------------------", wxNullBitmap);
+	}
+	if (!nonsys_presets.empty())
+	{
+		ui->Append("------------------------------------", wxNullBitmap);
+		for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
+			const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible;
+			ui->Append(it->first,
+				(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
+			if (it->first == selected)
+				ui->SetSelection(ui->GetCount() - 1);
+		}
+	}
     ui->Thaw();
 }
 
@@ -556,11 +584,11 @@ void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatibl
 				selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
 		}
 		if (preset.is_default)
-			ui->Append("______________________", wxNullBitmap);
+			ui->Append("------------------------------------", wxNullBitmap);
     }
 	if (!nonsys_presets.empty())
 	{
-		ui->Append("______________________", wxNullBitmap);
+		ui->Append("------------------------------------", wxNullBitmap);
 		for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
 			const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible;
 			ui->Append(it->first,
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 7131bf771..dcf10198d 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -986,6 +986,8 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
     // and draw a red flag in front of the selected preset.
     bool          wide_icons      = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr;
     assert(selected_preset != nullptr);
+	std::map<wxString, wxBitmap> nonsys_presets;
+	wxString selected_str = "";
     for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++ i) {
         const Preset &preset    = this->filaments.preset(i);
         bool          selected  = this->filament_presets[idx_extruder] == preset.name;
@@ -1023,10 +1025,36 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
                 (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16));
             bitmap = m_bitmapCache->insert(bitmap_key, bmps);
 		}
-		ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap);
-        if (selected)
-            ui->SetSelection(ui->GetCount() - 1);
+// 		ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap);
+//         if (selected)
+//             ui->SetSelection(ui->GetCount() - 1);
+
+		if (preset.is_default || preset.is_system){
+			ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), 
+				(bitmap == 0) ? wxNullBitmap : *bitmap);
+			if (selected)
+				ui->SetSelection(ui->GetCount() - 1);
+		}
+		else
+		{
+			nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), 
+				(bitmap == 0) ? wxNullBitmap : *bitmap);
+			if (selected)
+				selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
+		}
+		if (preset.is_default)
+			ui->Append("------------------------------------", wxNullBitmap);
     }
+
+	if (!nonsys_presets.empty())
+	{
+		ui->Append("------------------------------------", wxNullBitmap);
+		for (std::map<wxString, wxBitmap>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
+			ui->Append(it->first, it->second);
+			if (it->first == selected_str)
+				ui->SetSelection(ui->GetCount() - 1);
+		}
+	}
     ui->Thaw();
 }
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 1b70dfa5c..0b79f8229 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -38,8 +38,8 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 
 	// preset chooser
 	m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(270, -1), 0, 0,wxCB_READONLY);
-
-	m_cc_presets_choice = new wxComboCtrl(panel, wxID_ANY, L(""), wxDefaultPosition, wxDefaultSize/*wxSize(270, -1)*/, wxCB_READONLY);
+	/*
+	m_cc_presets_choice = new wxComboCtrl(panel, wxID_ANY, L(""), wxDefaultPosition, wxDefaultSize, wxCB_READONLY);
 	wxDataViewTreeCtrlComboPopup* popup = new wxDataViewTreeCtrlComboPopup;
 	if (popup != nullptr)
 	{
@@ -73,8 +73,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 		icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG));
 		icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG));
 	}
-
-
+*/
 	auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
 	//buttons
@@ -119,14 +118,16 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 	m_hsizer->AddSpacer(64);
 	m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
 	m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
-	m_hsizer->AddSpacer(64);
-	m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
+// 	m_hsizer->AddSpacer(64);
+// 	m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
 
 	//Horizontal sizer to hold the tree and the selected page.
 	m_hsizer = new wxBoxSizer(wxHORIZONTAL);
 	sizer->Add(m_hsizer, 1, wxEXPAND, 0);
 
 
+/*
+
 
 	//temporary left vertical sizer
 	m_left_sizer = new wxBoxSizer(wxVERTICAL);
@@ -153,7 +154,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
 		}
 	});
 
-
+*/
 
 	//left vertical sizer
 	m_left_sizer = new wxBoxSizer(wxVERTICAL);
@@ -202,8 +203,8 @@ void Tab::load_initial_data()
 {
 	m_config = &m_presets->get_edited_preset().config;
 	m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ?
-		"bullet_white.png" :
-		wxMSW ? "sys_unlock.png" : "lock_open.png";
+		"bullet_white.png" : "sys_unlock.png";
+// 		wxMSW ? "sys_unlock.png" : "lock_open.png";
 }
 
 PageShp Tab::add_options_page(wxString title, std::string icon, bool is_extruder_pages/* = false*/)
@@ -331,8 +332,8 @@ void Tab::update_changed_ui()
 	{
 		bool is_nonsys_value = false;
 		bool is_modified_value = true;
-		std::string sys_icon = wxMSW ? "sys_lock.png" : "lock.png";
-		std::string icon = wxMSW ? "action_undo.png" : "arrow_undo.png";
+		std::string sys_icon = /*wxMSW ? */"sys_lock.png"/* : "lock.png"*/;
+		std::string icon = /*wxMSW ? */"action_undo.png"/* : "arrow_undo.png"*/;
 		wxColour& color = *get_sys_label_clr();
 		if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) {
 			is_nonsys_value = true;
@@ -478,9 +479,9 @@ void Tab::update_changed_tree_ui()
 void Tab::update_undo_buttons()
 {
 	const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" :
-									wxMSW ? "action_undo.png" : "arrow_undo.png";
+									/*wxMSW ? */"action_undo.png"/* : "arrow_undo.png"*/;
 	const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon :
-									wxMSW ? "sys_lock.png" : "lock.png";
+									/*wxMSW ? */"sys_lock.png"/* : "lock.png"*/;
 
 	m_undo_btn->SetBitmap(wxBitmap(from_u8(var(undo_icon)), wxBITMAP_TYPE_PNG));
 	m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(undo_to_sys_icon)), wxBITMAP_TYPE_PNG));
@@ -552,8 +553,8 @@ void Tab::update_dirty(){
 void Tab::update_tab_ui()
 {
 	m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets);
-	update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets);
-	update_presetsctrl(m_presetctrl, m_show_incompatible_presets);
+// 	update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets);
+// 	update_presetsctrl(m_presetctrl, m_show_incompatible_presets);
 }
 
 // Load a provied DynamicConfig into the tab, modifying the active preset.
@@ -1786,7 +1787,7 @@ void Tab::load_current_preset()
 	const Preset* parent = m_presets->get_selected_preset_parent();
 	m_nonsys_btn_icon = parent == nullptr ?
 		"bullet_white.png" :
-		wxMSW ? "sys_unlock.png" : "lock_open.png";
+		/*wxMSW ? */"sys_unlock.png"/* : "lock_open.png"*/;
 
 	// use CallAfter because some field triggers schedule on_change calls using CallAfter,
 	// and we don't want them to be called after this update_dirty() as they would mark the 

From 723bd22b96ac23b1af3ee1438cc41f0dd86a70f9 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 11 Apr 2018 15:35:04 +0200
Subject: [PATCH 100/118] Changed icons for lock/unlock filament_presets on
 Plater tab

Experiment with filament_presets transparency on GTK
---
 xs/src/slic3r/GUI/BitmapCache.cpp  | 5 ++++-
 xs/src/slic3r/GUI/PresetBundle.cpp | 4 ++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/xs/src/slic3r/GUI/BitmapCache.cpp b/xs/src/slic3r/GUI/BitmapCache.cpp
index f92369650..e29687ce5 100644
--- a/xs/src/slic3r/GUI/BitmapCache.cpp
+++ b/xs/src/slic3r/GUI/BitmapCache.cpp
@@ -88,7 +88,10 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, std::vector<wxBitma
 
     wxMemoryDC memDC;
     memDC.SelectObject(*bitmap);
-    memDC.SetBackground(*wxTRANSPARENT_BRUSH);
+#ifdef __WXGTK__
+#else
+	memDC.SetBackground(*wxTRANSPARENT_BRUSH);
+#endif
     memDC.Clear();
     size_t x = 0;
 	for (wxBitmap &bmp : bmps) {
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index dcf10198d..74e1b60d3 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -254,8 +254,8 @@ bool PresetBundle::load_compatible_bitmaps()
 {
     const std::string path_bitmap_compatible   = "flag-green-icon.png";
     const std::string path_bitmap_incompatible = "flag-red-icon.png";
-    const std::string path_bitmap_lock         = "lock.png";
-    const std::string path_bitmap_lock_open    = "lock_open.png";
+    const std::string path_bitmap_lock         = "sys_lock.png";//"lock.png";
+	const std::string path_bitmap_lock_open    = "sys_unlock.png";//"lock_open.png";
     bool loaded_compatible   = m_bitmapCompatible  ->LoadFile(
         wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG);
     bool loaded_incompatible = m_bitmapIncompatible->LoadFile(

From 1a274a253abd9860f8b5a0c956ab2511ae997fef Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 12 Apr 2018 10:10:15 +0200
Subject: [PATCH 101/118] Fixed object/instance transformation after import
 from 3mf files

---
 xs/src/libslic3r/Format/3mf.cpp | 39 +++++----------------------------
 1 file changed, 6 insertions(+), 33 deletions(-)

diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 89f9b277f..7b5bf7e8a 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -388,7 +388,6 @@ namespace Slic3r {
 
         bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter);
 
-        void _apply_transform(ModelObject& object, const Matrix4x4& matrix);
         void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix);
 
         bool _handle_start_config(const char** attributes, unsigned int num_attributes);
@@ -557,19 +556,6 @@ namespace Slic3r {
 
             if (!_generate_volumes(*object.second, obj_geometry->second, *volumes_ptr))
                 return false;
-
-            // apply transformation if the object contains a single instance
-            if (object.second->instances.size() == 1)
-            {
-                for (const Instance& instance : m_instances)
-                {
-                    if (object.second->instances[0] == instance.instance)
-                    {
-                        _apply_transform(*object.second, instance.matrix);
-                        break;
-                    }
-                }
-            }
         }
 
         // fixes the min z of the model if negative
@@ -822,10 +808,9 @@ namespace Slic3r {
             if (instance.instance != nullptr)
             {
                 ModelObject* object = instance.instance->get_object();
-                if ((object != nullptr) && (object->instances.size() > 1))
+                if (object != nullptr)
                 {
-                    // multiple instances -> apply the matrix to the instance
-                    // (for single instance the transformation can be applied only after the volumes have been generated)
+                    // apply the matrix to the instance
                     _apply_transform(*instance.instance, instance.matrix);
                 }
             }
@@ -1120,15 +1105,6 @@ namespace Slic3r {
         return true;
     }
 
-    void _3MF_Importer::_apply_transform(ModelObject& object, const Matrix4x4& matrix)
-    {
-        float matrix3x4[12] = { matrix(0, 0), matrix(0, 1), matrix(0, 2), matrix(0, 3),
-                                matrix(1, 0), matrix(1, 1), matrix(1, 2), matrix(1, 3),
-                                matrix(2, 0), matrix(2, 1), matrix(2, 2), matrix(2, 3) };
-
-        object.transform(matrix3x4);
-    }
-
     void _3MF_Importer::_apply_transform(ModelInstance& instance, const Matrix4x4& matrix)
     {
         // slic3r ModelInstance cannot be transformed using a matrix
@@ -1645,9 +1621,7 @@ namespace Slic3r {
             }
 
             Eigen::Affine3f transform;
-            transform = Eigen::Translation3f((float)(instance->offset.x + object.origin_translation.x), (float)(instance->offset.y + object.origin_translation.y), (float)object.origin_translation.z)
-                        * Eigen::AngleAxisf((float)instance->rotation, Eigen::Vector3f::UnitZ())
-                        * Eigen::Scaling((float)instance->scaling_factor);
+            transform = Eigen::Translation3f((float)instance->offset.x, (float)instance->offset.y, 0.0f) * Eigen::AngleAxisf((float)instance->rotation, Eigen::Vector3f::UnitZ()) * Eigen::Scaling((float)instance->scaling_factor);
             build_items.emplace_back(instance_id, transform.matrix());
 
             stream << "  </" << OBJECT_TAG << ">\n";
@@ -1690,10 +1664,9 @@ namespace Slic3r {
             for (int i = 0; i < stl.stats.shared_vertices; ++i)
             {
                 stream << "     <" << VERTEX_TAG << " ";
-                // Subtract origin_translation in order to restore the original local coordinates
-                stream << "x=\"" << (stl.v_shared[i].x - object.origin_translation.x) << "\" ";
-                stream << "y=\"" << (stl.v_shared[i].y - object.origin_translation.y) << "\" ";
-                stream << "z=\"" << (stl.v_shared[i].z - object.origin_translation.z) << "\" />\n";
+                stream << "x=\"" << stl.v_shared[i].x << "\" ";
+                stream << "y=\"" << stl.v_shared[i].y << "\" ";
+                stream << "z=\"" << stl.v_shared[i].z << "\" />\n";
             }
         }
 

From b062cddeea4f6ef11557b6bb533a046076ba177d Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 12 Apr 2018 12:56:42 +0200
Subject: [PATCH 102/118] Fixed object cutting

---
 xs/src/libslic3r/Model.cpp | 30 +++---------------------------
 1 file changed, 3 insertions(+), 27 deletions(-)

diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 561106713..bda49421d 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -863,33 +863,9 @@ void ModelObject::cut(coordf_t z, Model* model) const
             lower->add_volume(*volume);
         } else {
             TriangleMesh upper_mesh, lower_mesh;
-            // TODO: shouldn't we use object bounding box instead of per-volume bb?
-            coordf_t cut_z = z + volume->mesh.bounding_box().min.z;
-            if (false) {
-//            if (volume->mesh.has_multiple_patches()) {
-                // Cutting algorithm does not work on intersecting meshes.
-                // As we are not sure whether the meshes don't intersect,
-                // we rather split the mesh into multiple non-intersecting pieces.
-                TriangleMeshPtrs meshptrs = volume->mesh.split();
-                for (TriangleMeshPtrs::iterator mesh = meshptrs.begin(); mesh != meshptrs.end(); ++mesh) {
-                    printf("Cutting mesh patch %d of %d\n", int(mesh - meshptrs.begin()), int(meshptrs.size()));
-                    (*mesh)->repair();
-                    TriangleMeshSlicer tms(*mesh);
-                    if (mesh == meshptrs.begin()) {
-                        tms.cut(cut_z, &upper_mesh, &lower_mesh);
-                    } else {
-                        TriangleMesh upper_mesh_this, lower_mesh_this;
-                        tms.cut(cut_z, &upper_mesh_this, &lower_mesh_this);
-                        upper_mesh.merge(upper_mesh_this);
-                        lower_mesh.merge(lower_mesh_this);
-                    }
-                    delete *mesh;
-                }
-            } else {
-                printf("Cutting mesh patch\n");
-                TriangleMeshSlicer tms(&volume->mesh);
-                tms.cut(cut_z, &upper_mesh, &lower_mesh);
-            }
+            printf("Cutting mesh patch\n");
+            TriangleMeshSlicer tms(&volume->mesh);
+            tms.cut(z, &upper_mesh, &lower_mesh);
 
             upper_mesh.repair();
             lower_mesh.repair();

From a3fc49ee76673ddcc9650baffc6a95c2f02ce144 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 12 Apr 2018 13:54:26 +0200
Subject: [PATCH 103/118] Fixed hovering not shown on out of print bed objects

---
 lib/Slic3r/GUI/3DScene.pm | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index a89ed0888..e5990fe8d 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -1828,7 +1828,6 @@ sub _fragment_shader_Gouraud {
     return <<'FRAGMENT';
 #version 110
 
-const vec4 OUTSIDE_COLOR = vec4(0.24, 0.42, 0.62, 1.0);
 const vec3 ZERO = vec3(0.0, 0.0, 0.0);
 
 // x = tainted, y = specular;
@@ -1841,11 +1840,9 @@ uniform vec4 uniform_color;
 
 void main()
 {
-    // if the fragment is outside the print volume use predefined color
-    vec4 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? OUTSIDE_COLOR : uniform_color;
-
-    gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 0.0) + color * intensity.x;
-    gl_FragColor.a = color.a;
+    // if the fragment is outside the print volume -> use darker color
+    vec3 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(uniform_color.rgb, ZERO, 0.3333) : uniform_color.rgb;
+    gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a);
 }
 
 FRAGMENT

From adbaa42b2b44c2d1bda43cd589fd83ed11476c6b Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 12 Apr 2018 15:38:05 +0200
Subject: [PATCH 104/118] Bugfix: cooling tubes parameters change invalidates
 generated wipe tower

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

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 3ce73ab9a..4012b91f8 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -90,8 +90,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
         "bridge_acceleration",
         "bridge_fan_speed",
         "cooling",
-        "cooling_tube_retraction",
-        "cooling_tube_length",
         "default_acceleration",
         "deretract_speed",
         "disable_fan_first_layers",
@@ -131,7 +129,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
         "notes",
         "only_retract_when_crossing_perimeters",
         "output_filename_format",
-        "parking_pos_retraction",
         "perimeter_acceleration",
         "post_process",
         "printer_notes",
@@ -202,6 +199,9 @@ 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 == "cooling_tube_retraction"
+            || opt_key == "cooling_tube_length"
             || opt_key == "z_offset") {
             steps.emplace_back(psWipeTower);
         } else if (

From 52a3f4a2bbce38aab69dab6a62a943525cf7f699 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 12 Apr 2018 16:13:59 +0200
Subject: [PATCH 105/118] Default purging volumes set to more appropriate
 values

---
 xs/src/libslic3r/PrintConfig.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index ef54b24cf..66576c9f1 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1038,7 +1038,7 @@ PrintConfigDef::PrintConfigDef()
 
     def = this->add("parking_pos_retraction", coFloat);
     def->label = L("Filament parking position");
-    def->tooltip = L("Distance of the extruder tip from the position where the filament is parked"
+    def->tooltip = L("Distance of the extruder tip from the position where the filament is parked "
                       "when unloaded. This should match the value in printer firmware. ");
     def->sidetext = L("mm");
     def->cli = "parking_pos_retraction=f";
@@ -1818,18 +1818,18 @@ PrintConfigDef::PrintConfigDef()
                      "wipe tower. These values are used to simplify creation of the full purging "
                      "volumes below. ");
     def->cli = "wiping-volumes-extruders=f@";
-    def->default_value = new ConfigOptionFloats { 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f, 50.f  };
+    def->default_value = new ConfigOptionFloats { 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f  };
 
     def = this->add("wiping_volumes_matrix", coFloats);
     def->label = L("Purging volumes - matrix");
     def->tooltip = L("This matrix describes volumes (in cubic milimetres) required to purge the"
                      " new filament on the wipe tower for any given pair of tools. ");
     def->cli = "wiping-volumes-matrix=f@";
-    def->default_value = new ConfigOptionFloats {   0.f, 100.f, 100.f, 100.f, 100.f,
-                                                  100.f,   0.f, 100.f, 100.f, 100.f,
-                                                  100.f, 100.f,   0.f, 100.f, 100.f,
-                                                  100.f, 100.f, 100.f,   0.f, 100.f,
-                                                  100.f, 100.f, 100.f, 100.f,   0.f };
+    def->default_value = new ConfigOptionFloats {   0.f, 140.f, 140.f, 140.f, 140.f,
+                                                  140.f,   0.f, 140.f, 140.f, 140.f,
+                                                  140.f, 140.f,   0.f, 140.f, 140.f,
+                                                  140.f, 140.f, 140.f,   0.f, 140.f,
+                                                  140.f, 140.f, 140.f, 140.f,   0.f };
 
     def = this->add("wipe_tower_x", coFloat);
     def->label = L("Position X");

From d82505984a3f63bcf66a472c54c520495421a8c3 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 12 Apr 2018 16:46:17 +0200
Subject: [PATCH 106/118] Rollback changes in BitmapCache

---
 xs/src/slic3r/GUI/BitmapCache.cpp | 3 ---
 xs/src/slic3r/GUI/BitmapCache.hpp | 5 +----
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/xs/src/slic3r/GUI/BitmapCache.cpp b/xs/src/slic3r/GUI/BitmapCache.cpp
index e29687ce5..acbcb5c73 100644
--- a/xs/src/slic3r/GUI/BitmapCache.cpp
+++ b/xs/src/slic3r/GUI/BitmapCache.cpp
@@ -88,10 +88,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, std::vector<wxBitma
 
     wxMemoryDC memDC;
     memDC.SelectObject(*bitmap);
-#ifdef __WXGTK__
-#else
 	memDC.SetBackground(*wxTRANSPARENT_BRUSH);
-#endif
     memDC.Clear();
     size_t x = 0;
 	for (wxBitmap &bmp : bmps) {
diff --git a/xs/src/slic3r/GUI/BitmapCache.hpp b/xs/src/slic3r/GUI/BitmapCache.hpp
index 46c7db3a1..0cf9d8acf 100644
--- a/xs/src/slic3r/GUI/BitmapCache.hpp
+++ b/xs/src/slic3r/GUI/BitmapCache.hpp
@@ -31,10 +31,7 @@ public:
 
 	static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
 	static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }
-	static wxBitmap mkclear(size_t width, size_t height) {
-		wxColour bg_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
-		return mksolid(width, height, bg_clr.Red(), bg_clr.Green(), bg_clr.Blue()/*, 0, 0, 0*/,  wxALPHA_TRANSPARENT);
-	}
+	static wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); }
 
 private:
     std::map<std::string, wxBitmap*>	m_map;

From acd8d2df98d5207c0fb8091c96a8d86a57980387 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 13 Apr 2018 09:01:48 +0200
Subject: [PATCH 107/118] Fixed layer height profile editing for multipart
 objects

---
 lib/Slic3r/GUI/3DScene.pm     | 12 ++++++------
 xs/src/slic3r/GUI/3DScene.cpp |  7 ++++++-
 xs/src/slic3r/GUI/3DScene.hpp |  5 +----
 3 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index e5990fe8d..13460cfed 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -1527,10 +1527,13 @@ sub draw_active_object_annotations {
     my ($reset_left, $reset_bottom, $reset_right, $reset_top) = $self->_variable_layer_thickness_reset_rect_viewport;
     my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative;
 
+    my $print_object = $self->{print}->get_object($object_idx);
+    my $z_max = $print_object->model_object->bounding_box->z_max;
+    
     $self->{layer_height_edit_shader}->enable;
     $self->{layer_height_edit_shader}->set_uniform('z_to_texture_row',            $volume->layer_height_texture_z_to_row_id);
     $self->{layer_height_edit_shader}->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height);
-    $self->{layer_height_edit_shader}->set_uniform('z_cursor',                    $volume->bounding_box->z_max * $z_cursor_relative);
+    $self->{layer_height_edit_shader}->set_uniform('z_cursor',                    $z_max * $z_cursor_relative);
     $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width',         $self->{layer_height_edit_band_width});
     glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
     glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 
@@ -1552,8 +1555,8 @@ sub draw_active_object_annotations {
     glBegin(GL_QUADS);
     glVertex3f($bar_left,  $bar_bottom, 0);
     glVertex3f($bar_right, $bar_bottom, 0);
-    glVertex3f($bar_right, $bar_top, $volume->bounding_box->z_max);
-    glVertex3f($bar_left,  $bar_top, $volume->bounding_box->z_max);
+    glVertex3f($bar_right, $bar_top, $z_max);
+    glVertex3f($bar_left,  $bar_top, $z_max);
     glEnd();
     glBindTexture(GL_TEXTURE_2D, 0);
     $self->{layer_height_edit_shader}->disable;
@@ -1572,7 +1575,6 @@ sub draw_active_object_annotations {
 
     # Paint the graph.
     #FIXME show some kind of legend.
-    my $print_object = $self->{print}->get_object($object_idx);
     my $max_z = unscale($print_object->size->z);
     my $profile = $print_object->model_object->layer_height_profile;
     my $layer_height = $print_object->config->get('layer_height');
@@ -1934,8 +1936,6 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
 
 #define INTENSITY_AMBIENT    0.3
 
-uniform float z_to_texture_row;
-
 // x = tainted, y = specular;
 varying vec2 intensity;
 
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 1ce3d76bf..a33ef4d1c 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -305,7 +305,7 @@ void GLVolume::render_using_layer_height() const
         glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height()));
 
     if (z_cursor_id >= 0)
-        glUniform1f(z_cursor_id, (GLfloat)(bounding_box.max.z * layer_height_texture_data.z_cursor_relative));
+        glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max.z * layer_height_texture_data.z_cursor_relative));
 
     if (z_cursor_band_width_id >= 0)
         glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
@@ -327,6 +327,11 @@ void GLVolume::render_using_layer_height() const
         glUseProgram(current_program_id);
 }
 
+double GLVolume::layer_height_texture_z_to_row_id() const
+{
+    return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max.z);
+}
+
 void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force)
 {
     GLTexture *tex = this->layer_height_texture.get();
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index 6486a7f0e..380206dd9 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -361,10 +361,7 @@ public:
         return (layer_height_texture.get() == nullptr) ? 0 :
             (void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4);
     }
-    double              layer_height_texture_z_to_row_id() const { 
-        return (this->layer_height_texture.get() == nullptr) ? 0. : 
-            double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * bounding_box.max.z);
-    }
+    double              layer_height_texture_z_to_row_id() const;
     void                generate_layer_height_texture(PrintObject *print_object, bool force);
 
     void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, PrintObject* print_object, float z_cursor_relative, float edit_band_width)

From 7b29ecb15cf450347cedece3d3445449b2f57c1f Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 13 Apr 2018 12:15:43 +0200
Subject: [PATCH 108/118] Fixed cutting plane z in 3D view when object is
 scaled

---
 lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 0b5312b30..7f930c1d0 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -244,7 +244,7 @@ sub _update {
             $self->{canvas}->reset_objects;
             $self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
             $self->{canvas}->SetCuttingPlane(
-                $self->{cut_options}{z},
+                $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor,
                 [@expolygons],
             );
             $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config});

From 120c1978ae9c389393ad3332c2077d5ba255353a Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 13 Apr 2018 12:35:04 +0200
Subject: [PATCH 109/118] Cleaned code

---
 xs/src/slic3r/GUI/Field.cpp        | 31 +++++-------------
 xs/src/slic3r/GUI/Field.hpp        | 52 +++++++++++++++++-------------
 xs/src/slic3r/GUI/GUI.cpp          | 25 ++++++++------
 xs/src/slic3r/GUI/GUI.hpp          |  8 ++---
 xs/src/slic3r/GUI/OptionsGroup.cpp | 33 +++++++------------
 xs/src/slic3r/GUI/OptionsGroup.hpp | 36 ++++++++++-----------
 xs/src/slic3r/GUI/Tab.cpp          | 45 ++++++++++++--------------
 xs/src/slic3r/GUI/Tab.hpp          | 26 +++++++--------
 8 files changed, 120 insertions(+), 136 deletions(-)

diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp
index a7fd8d29e..c68b2304c 100644
--- a/xs/src/slic3r/GUI/Field.cpp
+++ b/xs/src/slic3r/GUI/Field.cpp
@@ -75,13 +75,13 @@ namespace Slic3r { namespace GUI {
 		return tooltip_text;
 	}
 
-	bool Field::is_matched(std::string string, std::string pattern)
+	bool Field::is_matched(const std::string& string, const std::string& pattern)
 	{
 		std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl
 		return std::regex_match(string, regex_pattern);
 	}
 
-	boost::any Field::get_value_by_opt_type(wxString str)
+	boost::any Field::get_value_by_opt_type(wxString& str)
 	{
 		boost::any ret_val;
 		switch (m_opt.type){
@@ -377,7 +377,7 @@ void Choice::set_selection()
 	}
 }
 
-void Choice::set_value(const std::string value, bool change_event)  //! Redundant?
+void Choice::set_value(const std::string& value, bool change_event)  //! Redundant?
 {
 	m_disable_change_event = !change_event;
 
@@ -396,7 +396,7 @@ void Choice::set_value(const std::string value, bool change_event)  //! Redundan
 	m_disable_change_event = false;
 }
 
-void Choice::set_value(boost::any value, bool change_event)
+void Choice::set_value(const boost::any& value, bool change_event)
 {
 	m_disable_change_event = !change_event;
 
@@ -435,7 +435,7 @@ void Choice::set_value(boost::any value, bool change_event)
 }
 
 //! it's needed for _update_serial_ports()
-void Choice::set_values(const std::vector<std::string> values)
+void Choice::set_values(const std::vector<std::string>& values)
 {
 	if (values.empty())
 		return;
@@ -554,7 +554,7 @@ void PointCtrl::BUILD()
 	y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
 }
 
-void PointCtrl::set_value(const Pointf value, bool change_event)
+void PointCtrl::set_value(const Pointf& value, bool change_event)
 {
 	m_disable_change_event = !change_event;
 
@@ -566,10 +566,10 @@ void PointCtrl::set_value(const Pointf value, bool change_event)
 	m_disable_change_event = false;
 }
 
-void PointCtrl::set_value(boost::any value, bool change_event)
+void PointCtrl::set_value(const boost::any& value, bool change_event)
 {
 	Pointf pt;
-	Pointf *ptf = boost::any_cast<Pointf>(&value);
+	const Pointf *ptf = boost::any_cast<Pointf>(&value);
 	if (!ptf)
 	{
 		ConfigOptionPoints* pts = boost::any_cast<ConfigOptionPoints*>(value);
@@ -577,21 +577,6 @@ void PointCtrl::set_value(boost::any value, bool change_event)
 	}
 	else
 		pt = *ptf;
-// 	try
-// 	{
-// 		pt = boost::any_cast<ConfigOptionPoints*>(value)->values.at(0);
-// 	}
-// 	catch (const std::exception &e)
-// 	{
-// 		try{
-// 			pt = boost::any_cast<Pointf>(value);
-// 		}
-// 		catch (const std::exception &e)
-// 		{
-// 			std::cerr << "Error! Can't cast PointCtrl value" << m_opt_id << "\n";
-// 			return;
-// 		}		
-// 	}	
 	set_value(pt, change_event);
 }
 
diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp
index da3e23ccd..cdc7c0d81 100644
--- a/xs/src/slic3r/GUI/Field.hpp
+++ b/xs/src/slic3r/GUI/Field.hpp
@@ -31,8 +31,8 @@ namespace Slic3r { namespace GUI {
 class Field;
 using t_field = std::unique_ptr<Field>;
 using t_kill_focus = std::function<void()>;
-using t_change = std::function<void(t_config_option_key, boost::any)>;
-using t_back_to_init = std::function<void(std::string)>;
+using t_change = std::function<void(t_config_option_key, const boost::any&)>;
+using t_back_to_init = std::function<void(const std::string&)>;
 
 wxString double_to_string(double const value);
 
@@ -81,7 +81,7 @@ public:
     /// Sets a value for this control.
     /// subclasses should overload with a specific version
     /// Postcondition: Method does not fire the on_change event.
-    virtual void		set_value(boost::any value, bool change_event) = 0;
+    virtual void		set_value(const boost::any& value, bool change_event) = 0;
     
     /// Gets a boost::any representing this control.
     /// subclasses should overload with a specific version
@@ -100,7 +100,7 @@ public:
 	virtual wxString	get_tooltip_text(const wxString& default_string);
 
     // set icon to "UndoToSystemValue" button according to an inheritance of preset
-	void set_nonsys_btn_icon(const std::string& icon);
+	void				set_nonsys_btn_icon(const std::string& icon);
 
     Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {};
     Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {};
@@ -109,8 +109,8 @@ public:
     virtual wxSizer*	getSizer()  { return nullptr; }
     virtual wxWindow*	getWindow() { return nullptr; }
 
-	bool		is_matched(std::string string, std::string pattern);
-	boost::any	get_value_by_opt_type(wxString str);
+	bool				is_matched(const std::string& string, const std::string& pattern);
+	boost::any			get_value_by_opt_type(wxString& str);
 
     /// Factory method for generating new derived classes.
     template<class T>
@@ -137,16 +137,17 @@ class TextCtrl : public Field {
 public:
 	TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt,  id) {}
 	TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
+	~TextCtrl() {}
 
     void BUILD();
     wxWindow* window {nullptr};
 
-    virtual void	set_value(std::string value, bool change_event = false) {
+    virtual void	set_value(const std::string& value, bool change_event = false) {
 		m_disable_change_event = !change_event;
         dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
 		m_disable_change_event = false;
     }
-    virtual void	set_value(boost::any value, bool change_event = false) {
+	virtual void	set_value(const boost::any& value, bool change_event = false) {
 		m_disable_change_event = !change_event;
 		dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
 		m_disable_change_event = false;
@@ -164,6 +165,7 @@ class CheckBox : public Field {
 public:
 	CheckBox(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
 	CheckBox(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
+	~CheckBox() {}
 
 	wxWindow*		window{ nullptr };
 	void			BUILD() override;
@@ -173,7 +175,7 @@ public:
 		dynamic_cast<wxCheckBox*>(window)->SetValue(value);
 		m_disable_change_event = false;
 	}
-	void			set_value(boost::any value, bool change_event = false) {
+	void			set_value(const boost::any& value, bool change_event = false) {
 		m_disable_change_event = !change_event;
 		dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
 		m_disable_change_event = false;
@@ -190,21 +192,22 @@ class SpinCtrl : public Field {
 public:
 	SpinCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id), tmp_value(-9999) {}
 	SpinCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id), tmp_value(-9999) {}
+	~SpinCtrl() {}
 
 	int				tmp_value;
 
 	wxWindow*		window{ nullptr };
 	void			BUILD() override;
 
-	void			set_value(const std::string value, bool change_event = false) {
+	void			set_value(const std::string& value, bool change_event = false) {
 		m_disable_change_event = !change_event;
 		dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
 		m_disable_change_event = false;
 	}
-	void			set_value(boost::any value, bool change_event = false) {
+	void			set_value(const boost::any& value, bool change_event = false) {
 		m_disable_change_event = !change_event;
 		tmp_value = boost::any_cast<int>(value);
-		dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value/*boost::any_cast<int>(value)*/);
+		dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
 		m_disable_change_event = false;
 	}
 	boost::any		get_value() override {
@@ -221,14 +224,15 @@ class Choice : public Field {
 public:
 	Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
 	Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
+	~Choice() {}
 
 	wxWindow*		window{ nullptr };
 	void			BUILD() override;
 
 	void			set_selection();
-	void			set_value(const std::string value, bool change_event = false);
-	void			set_value(boost::any value, bool change_event = false);
-	void			set_values(const std::vector<std::string> values);
+	void			set_value(const std::string& value, bool change_event = false);
+	void			set_value(const boost::any& value, bool change_event = false);
+	void			set_values(const std::vector<std::string> &values);
 	boost::any		get_value() override;
 
 	void			enable() override { dynamic_cast<wxComboBox*>(window)->Enable(); };
@@ -241,16 +245,17 @@ class ColourPicker : public Field {
 public:
 	ColourPicker(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
 	ColourPicker(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
+	~ColourPicker() {}
 
 	wxWindow*		window{ nullptr };
 	void			BUILD()  override;
 
-	void			set_value(const std::string value, bool change_event = false) {
+	void			set_value(const std::string& value, bool change_event = false) {
 		m_disable_change_event = !change_event;
 		dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
 		m_disable_change_event = false;
 	 	}
-	void			set_value(boost::any value, bool change_event = false) {
+	void			set_value(const boost::any& value, bool change_event = false) {
 		m_disable_change_event = !change_event;
 		dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
 		m_disable_change_event = false;
@@ -268,23 +273,24 @@ class PointCtrl : public Field {
 public:
 	PointCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
 	PointCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
+	~PointCtrl() {}
 
 	wxSizer*		sizer{ nullptr };
-	wxTextCtrl*		x_textctrl;
-	wxTextCtrl*		y_textctrl;
+	wxTextCtrl*		x_textctrl{ nullptr };
+	wxTextCtrl*		y_textctrl{ nullptr };
 
 	void			BUILD()  override;
 
-	void			set_value(const Pointf value, bool change_event = false);
-	void			set_value(boost::any value, bool change_event = false);
+	void			set_value(const Pointf& value, bool change_event = false);
+	void			set_value(const boost::any& value, bool change_event = false);
 	boost::any		get_value() override;
 
 	void			enable() override {
 		x_textctrl->Enable();
-		y_textctrl->Enable(); };
+		y_textctrl->Enable(); }
 	void			disable() override{
 		x_textctrl->Disable();
-		y_textctrl->Disable(); };
+		y_textctrl->Disable(); }
 	wxSizer*		getSizer() override { return sizer; }
 };
 
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 9450f44ae..794e873d5 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -403,7 +403,7 @@ TabIface* get_preset_tab_iface(char *name)
 }
 
 // opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)
-void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index /*= 0*/)
+void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
 {
 	try{
 		switch (config.def()->get(opt_key)->type){
@@ -439,11 +439,18 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b
 			config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
 			break;
 		case coStrings:{
-			if (opt_key.compare("compatible_printers") == 0 ||
-				config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){
-				config.option<ConfigOptionStrings>(opt_key)->values.resize(0);
-				std::vector<std::string> values = boost::any_cast<std::vector<std::string>>(value);
-				if (values.size() == 1 && values[0] == "")
+			if (opt_key.compare("compatible_printers") == 0) {
+				config.option<ConfigOptionStrings>(opt_key)->values = 
+					boost::any_cast<std::vector<std::string>>(value);
+			}
+			else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){
+				std::string str = boost::any_cast<std::string>(value);
+				if (str.back() == ';') str.pop_back();
+				// Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
+				// Currently used for the post_process config value only.
+				std::vector<std::string> values;
+				boost::split(values, str, boost::is_any_of(";"));
+				if (values.size() == 1 && values[0] == "") 
 					break;
 				config.option<ConfigOptionStrings>(opt_key)->values = values;
 			}
@@ -510,17 +517,17 @@ void add_created_tab(Tab* panel)
 	g_wxTabPanel->AddPage(panel, panel->title());
 }
 
-void show_error(wxWindow* parent, wxString message){
+void show_error(wxWindow* parent, const wxString& message){
 	auto msg_wingow = new wxMessageDialog(parent, message, _(L("Error")), wxOK | wxICON_ERROR);
 	msg_wingow->ShowModal();
 }
 
-void show_info(wxWindow* parent, wxString message, wxString title){
+void show_info(wxWindow* parent, const wxString& message, const wxString& title){
 	auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION);
 	msg_wingow->ShowModal();
 }
 
-void warning_catcher(wxWindow* parent, wxString message){
+void warning_catcher(wxWindow* parent, const wxString& message){
 	if (message == _(L("GLUquadricObjPtr | Attempt to free unreferenced scalar")) )
 		return;
 	auto msg = new wxMessageDialog(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 24c3ec3f4..d92246c50 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -96,11 +96,11 @@ TabIface* get_preset_tab_iface(char *name);
 // add it at the end of the tab panel.
 void add_created_tab(Tab* panel);
 // Change option value in config
-void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index = 0);
+void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
 
-void show_error(wxWindow* parent, wxString message);
-void show_info(wxWindow* parent, wxString message, wxString title);
-void warning_catcher(wxWindow* parent, wxString message);
+void show_error(wxWindow* parent, const wxString& message);
+void show_info(wxWindow* parent, const wxString& message, const wxString& title);
+void warning_catcher(wxWindow* parent, const wxString& message);
 
 // load language saved at application config 
 bool load_language();
diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index 24e1ddf2e..8e2268965 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -227,12 +227,12 @@ Line OptionsGroup::create_single_option_line(const Option& option) const {
     return retval;
 }
 
-void OptionsGroup::on_change_OG(t_config_option_key id, /*config_value*/boost::any value) {
+void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) {
 	if (m_on_change != nullptr)
-		m_on_change(id, value);
+		m_on_change(opt_id, value);
 }
 
-Option ConfigOptionsGroup::get_option(const std::string opt_key, int opt_index /*= -1*/)
+Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/)
 {
 	if (!m_config->has(opt_key)) {
 		std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.";
@@ -245,7 +245,7 @@ Option ConfigOptionsGroup::get_option(const std::string opt_key, int opt_index /
 	return Option(*m_config->def()->get(opt_key), opt_id);
 }
 
-void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any value)
+void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value)
 {
 	if (!m_opt_map.empty())
 	{
@@ -268,16 +268,7 @@ void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any val
 			if (opt_index != -1){
 				// 		die "Can't set serialized option indexed value" ;
 			}
-			// 		# Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
-			// 		# Currently used for the post_process config value only.
-			// 		my @values = split / ; / , $field_value;
-			// 		$self->config->set($opt_key, \@values);
-			std::string str = boost::any_cast<std::string>(value);
-			if (str.back() == ';')
-				str.pop_back();
-			std::vector<std::string> values;
-			boost::split(values, str, boost::is_any_of(";"));
-			change_opt_value(*m_config, opt_key, values);
+			change_opt_value(*m_config, opt_key, value);
 		}
 		else {
 			if (opt_index == -1) {
@@ -297,14 +288,14 @@ void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any val
 	OptionsGroup::on_change_OG(opt_id, value); //!? Why doing this
 }
 
-void ConfigOptionsGroup::back_to_initial_value(const std::string opt_key)
+void ConfigOptionsGroup::back_to_initial_value(const std::string& opt_key)
 {
 	if (m_get_initial_config == nullptr)
 		return;
 	back_to_config_value(m_get_initial_config(), opt_key);
 }
 
-void ConfigOptionsGroup::back_to_sys_value(const std::string opt_key)
+void ConfigOptionsGroup::back_to_sys_value(const std::string& opt_key)
 {
 	if (m_get_sys_config == nullptr)
 		return;
@@ -313,7 +304,7 @@ void ConfigOptionsGroup::back_to_sys_value(const std::string opt_key)
 	back_to_config_value(m_get_sys_config(), opt_key);
 }
 
-void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string opt_key)
+void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key)
 {
 	boost::any value;
 	if (opt_key == "extruders_count"){
@@ -348,7 +339,7 @@ void ConfigOptionsGroup::reload_config(){
 
 }
 
-boost::any ConfigOptionsGroup::config_value(std::string opt_key, int opt_index, bool deserialize){
+boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize){
 
 	if (deserialize) {
 		// Want to edit a vector value(currently only multi - strings) in a single edit box.
@@ -365,7 +356,7 @@ boost::any ConfigOptionsGroup::config_value(std::string opt_key, int opt_index,
 	}
 }
 
-boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config, std::string opt_key, int opt_index /*= -1*/)
+boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index /*= -1*/)
 {
 	size_t idx = opt_index == -1 ? 0 : opt_index;
 	
@@ -457,7 +448,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 	return ret;
 }
 
-Field* ConfigOptionsGroup::get_fieldc(t_config_option_key opt_key, int opt_index){
+Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index){
 	Field* field = get_field(opt_key);
 	if (field != nullptr)
 		return field;
@@ -471,7 +462,7 @@ Field* ConfigOptionsGroup::get_fieldc(t_config_option_key opt_key, int opt_index
 	return opt_id.empty() ? nullptr : get_field(opt_id);
 }
 
-void ogStaticText::SetText(wxString value)
+void ogStaticText::SetText(const wxString& value)
 {
 	SetLabel(value);
 	Wrap(400);
diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp
index e58d9c9a9..b5846f24d 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.hpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.hpp
@@ -98,16 +98,16 @@ public:
     void		append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); }
 
     // return a non-owning pointer reference 
-    inline Field*	get_field(t_config_option_key id) const{
+    inline Field*	get_field(const t_config_option_key& id) const{
 							if (m_fields.find(id) == m_fields.end()) return nullptr;
 							return m_fields.at(id).get();
     }
-	bool			set_value(t_config_option_key id, boost::any value, bool change_event = false) {
+	bool			set_value(const t_config_option_key& id, const boost::any& value, bool change_event = false) {
 							if (m_fields.find(id) == m_fields.end()) return false;
 							m_fields.at(id)->set_value(value, change_event);
 							return true;
     }
-	boost::any		get_value(t_config_option_key id) {
+	boost::any		get_value(const t_config_option_key& id) {
 							boost::any out; 
     						if (m_fields.find(id) == m_fields.end()) ;
 							else 
@@ -118,7 +118,7 @@ public:
 	inline void		enable() { for (auto& field : m_fields) field.second->enable(); }
     inline void		disable() { for (auto& field : m_fields) field.second->disable(); }
 
-    OptionsGroup(wxWindow* _parent, wxString title, bool is_tab_opt=false) : 
+    OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) : 
 		m_parent(_parent), title(title), m_is_tab_opt(is_tab_opt), staticbox(title!="") {
         sizer = (staticbox ? new wxStaticBoxSizer(new wxStaticBox(_parent, wxID_ANY, title), wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
         auto num_columns = 1U;
@@ -152,14 +152,14 @@ protected:
 	const t_field&		build_field(const Option& opt, wxStaticText* label = nullptr);
 
     virtual void		on_kill_focus (){};
-	virtual void		on_change_OG(t_config_option_key opt_id, boost::any value);
-	virtual void		back_to_initial_value(const std::string opt_key){};
-	virtual void		back_to_sys_value(const std::string opt_key){};
+	virtual void		on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
+	virtual void		back_to_initial_value(const std::string& opt_key){}
+	virtual void		back_to_sys_value(const std::string& opt_key){}
 };
 
 class ConfigOptionsGroup: public OptionsGroup {
 public:
-	ConfigOptionsGroup(wxWindow* parent, wxString title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) :
+	ConfigOptionsGroup(wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) :
 		OptionsGroup(parent, title, is_tab_opt), m_config(_config) {}
 
     /// reference to libslic3r config, non-owning pointer (?).
@@ -167,8 +167,8 @@ public:
     bool					m_full_labels {0};
 	t_opt_map				m_opt_map;
 
-	Option		get_option(const std::string opt_key, int opt_index = -1);
-	Line		create_single_option_line(const std::string title, int idx = -1) /*const*/{
+	Option		get_option(const std::string& opt_key, int opt_index = -1);
+	Line		create_single_option_line(const std::string& title, int idx = -1) /*const*/{
 		Option option = get_option(title, idx);
 		return OptionsGroup::create_single_option_line(option);
 	}
@@ -181,16 +181,16 @@ public:
 		append_single_option_line(option);		
 	}
 
-	void		on_change_OG(t_config_option_key opt_id, boost::any value) override;
-	void		back_to_initial_value(const std::string opt_key) override;
-	void		back_to_sys_value(const std::string opt_key) override;
-	void back_to_config_value(const DynamicPrintConfig& config, const std::string opt_key);
+	void		on_change_OG(const t_config_option_key& opt_id, const boost::any& value) override;
+	void		back_to_initial_value(const std::string& opt_key) override;
+	void		back_to_sys_value(const std::string& opt_key) override;
+	void		back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key);
 	void		on_kill_focus() override{ reload_config();}
 	void		reload_config();
-	boost::any	config_value(std::string opt_key, int opt_index, bool deserialize);
+	boost::any	config_value(const std::string& opt_key, int opt_index, bool deserialize);
 	// return option value from config 
-	boost::any get_config_value(const DynamicPrintConfig& config, std::string opt_key, int opt_index = -1);
-	Field*		get_fieldc(t_config_option_key opt_key, int opt_index);
+	boost::any	get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index = -1);
+	Field*		get_fieldc(const t_config_option_key& opt_key, int opt_index);
 };
 
 //  Static text shown among the options.
@@ -200,7 +200,7 @@ public:
 	ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){}
 	~ogStaticText(){}
 
-	void SetText(wxString value);
+	void		SetText(const wxString& value);
 };
 
 }}
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index ebcd4af88..a9faf4c59 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -203,11 +203,10 @@ void Tab::load_initial_data()
 {
 	m_config = &m_presets->get_edited_preset().config;
 	m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ?
-		"bullet_white.png" : "sys_unlock.png";
-// 		wxMSW ? "sys_unlock.png" : "lock_open.png";
+						"bullet_white.png" : "sys_unlock.png";
 }
 
-PageShp Tab::add_options_page(wxString title, std::string icon, bool is_extruder_pages/* = false*/)
+PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/)
 {
 	// Index of icon in an icon list $self->{icons}.
 	auto icon_idx = 0;
@@ -332,8 +331,8 @@ void Tab::update_changed_ui()
 	{
 		bool is_nonsys_value = false;
 		bool is_modified_value = true;
-		std::string sys_icon = /*wxMSW ? */"sys_lock.png"/* : "lock.png"*/;
-		std::string icon = /*wxMSW ? */"action_undo.png"/* : "arrow_undo.png"*/;
+		std::string sys_icon = "sys_lock.png";
+		std::string icon = "action_undo.png";
 		wxColour color = get_sys_label_clr();
 		if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) {
 			is_nonsys_value = true;
@@ -478,10 +477,8 @@ void Tab::update_changed_tree_ui()
 
 void Tab::update_undo_buttons()
 {
-	const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" :
-									/*wxMSW ? */"action_undo.png"/* : "arrow_undo.png"*/;
-	const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon :
-									/*wxMSW ? */"sys_lock.png"/* : "lock.png"*/;
+	const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" : "action_undo.png";
+	const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon : "sys_lock.png";
 
 	m_undo_btn->SetBitmap(wxBitmap(from_u8(var(undo_icon)), wxBITMAP_TYPE_PNG));
 	m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(undo_to_sys_icon)), wxBITMAP_TYPE_PNG));
@@ -559,7 +556,7 @@ void Tab::update_tab_ui()
 
 // Load a provied DynamicConfig into the tab, modifying the active preset.
 // This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view.
-void Tab::load_config(DynamicPrintConfig config)
+void Tab::load_config(const DynamicPrintConfig& config)
 {
 	bool modified = 0;
 	for(auto opt_key : m_config->diff(config)) {
@@ -582,7 +579,7 @@ void Tab::reload_config(){
  	Thaw();
 }
 
-Field* Tab::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) const
+Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const
 {
 	Field* field = nullptr;
 	for (auto page : m_pages){
@@ -596,7 +593,7 @@ Field* Tab::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) const
 // Set a key/value pair on this page. Return true if the value has been modified.
 // Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer
 // after a preset is loaded.
-bool Tab::set_value(t_config_option_key opt_key, boost::any value){
+bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value){
 	bool changed = false;
 	for(auto page: m_pages) {
 		if (page->set_value(opt_key, value))
@@ -607,7 +604,7 @@ bool Tab::set_value(t_config_option_key opt_key, boost::any value){
 
 // To be called by custom widgets, load a value into a config,
 // update the preset selection boxes (the dirty flags)
-void Tab::load_key_value(std::string opt_key, boost::any value)
+void Tab::load_key_value(const std::string& opt_key, const boost::any& value)
 {
 	change_opt_value(*m_config, opt_key, value);
 	// Mark the print & filament enabled if they are compatible with the currently selected preset.
@@ -621,7 +618,7 @@ void Tab::load_key_value(std::string opt_key, boost::any value)
 
 extern wxFrame *g_wxMainFrame;
 
-void Tab::on_value_change(std::string opt_key, boost::any value)
+void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
 {
 	if (m_event_value_change > 0) {
 		wxCommandEvent event(m_event_value_change);
@@ -636,8 +633,8 @@ void Tab::on_value_change(std::string opt_key, boost::any value)
 	}
 	if (opt_key == "fill_density")
 	{
-		value = get_optgroup()->get_config_value(*m_config, opt_key);
-		get_optgroup()->set_value(opt_key, value);
+		boost::any val = get_optgroup()->get_config_value(*m_config, opt_key);
+		get_optgroup()->set_value(opt_key, val);
 	}
 	if (opt_key == "support_material" || opt_key == "support_material_buildplate_only")
 	{
@@ -1785,9 +1782,7 @@ void Tab::load_current_preset()
 	// Reload preset pages with the new configuration values.
 	reload_config();
 	const Preset* parent = m_presets->get_selected_preset_parent();
-	m_nonsys_btn_icon = parent == nullptr ?
-		"bullet_white.png" :
-		/*wxMSW ? */"sys_unlock.png"/* : "lock_open.png"*/;
+	m_nonsys_btn_icon = parent == nullptr ? "bullet_white.png" : "sys_unlock.png";
 
 	// use CallAfter because some field triggers schedule on_change calls using CallAfter,
 	// and we don't want them to be called after this update_dirty() as they would mark the 
@@ -1844,7 +1839,7 @@ void Tab::rebuild_page_tree()
 // Called by the UI combo box when the user switches profiles.
 // Select a preset by a name.If !defined(name), then the default preset is selected.
 // If the current profile is modified, user is asked to save the changes.
-void Tab::select_preset(std::string preset_name /*= ""*/)
+void Tab::select_preset(const std::string& preset_name /*= ""*/)
 {
 	std::string name = preset_name;
 	auto force = false;
@@ -1911,7 +1906,7 @@ void Tab::select_preset(std::string preset_name /*= ""*/)
 
 // If the current preset is dirty, the user is asked whether the changes may be discarded.
 // if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned.
-bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, std::string new_printer_name /*= ""*/)
+bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/)
 {
 	if (presets == nullptr) presets = m_presets;
 	// Display a dialog showing the dirty options in a human readable form.
@@ -2339,7 +2334,7 @@ void Page::reload_config()
 		group->reload_config();
 }
 
-Field* Page::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) const
+Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*/) const
 {
 	Field* field = nullptr;
 	for (auto opt : m_optgroups){
@@ -2350,7 +2345,7 @@ Field* Page::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) cons
 	return field;
 }
 
-bool Page::set_value(t_config_option_key opt_key, boost::any value){
+bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value){
 	bool changed = false;
 	for(auto optgroup: m_optgroups) {
 		if (optgroup->set_value(opt_key, value))
@@ -2360,7 +2355,7 @@ bool Page::set_value(t_config_option_key opt_key, boost::any value){
 }
 
 // package Slic3r::GUI::Tab::Page;
-ConfigOptionsGroupShp Page::new_optgroup(wxString title, int noncommon_label_width /*= -1*/)
+ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_label_width /*= -1*/)
 {
 	//! config_ have to be "right"
 	ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(this, title, m_config, true);
@@ -2401,7 +2396,7 @@ ConfigOptionsGroupShp Page::new_optgroup(wxString title, int noncommon_label_wid
 	return optgroup;
 }
 
-void SavePresetWindow::build(wxString title, std::string default_name, std::vector<std::string> &values)
+void SavePresetWindow::build(const wxString& title, const std::string& default_name, std::vector<std::string> &values)
 {
 	auto text = new wxStaticText(this, wxID_ANY, _(L("Save ")) + title + _(L(" as:")), 
 									wxDefaultPosition, wxDefaultSize);
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 8a2c38e05..9465f86b4 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -68,9 +68,9 @@ public:
 	size_t		iconID() const { return m_iconID; }
 	void		set_config(DynamicPrintConfig* config_in) { m_config = config_in; }
 	void		reload_config();
-	Field*		get_field(t_config_option_key opt_key, int opt_index = -1) const;
-	bool		set_value(t_config_option_key opt_key, boost::any value);
-	ConfigOptionsGroupShp	new_optgroup(wxString title, int noncommon_label_width = -1);
+	Field*		get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
+	bool		set_value(const t_config_option_key& opt_key, const boost::any& value);
+	ConfigOptionsGroupShp	new_optgroup(const wxString& title, int noncommon_label_width = -1);
 };
 
 // Slic3r::GUI::Tab;
@@ -129,7 +129,7 @@ public:
 
 public:
 	Tab() {}
-	Tab(wxNotebook* parent, wxString title, const char* name, bool no_controller) : 
+	Tab(wxNotebook* parent, const wxString& title, const char* name, bool no_controller) : 
 		m_parent(parent), m_title(title), m_name(name), m_no_controller(no_controller) {
 		Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
 		get_tabs_list().push_back(this);
@@ -147,12 +147,12 @@ public:
 	void		create_preset_tab(PresetBundle *preset_bundle);
 	void		load_current_preset();
 	void		rebuild_page_tree();
-	void		select_preset(std::string preset_name = "");
-	bool		may_discard_current_dirty_preset(PresetCollection* presets = nullptr, std::string new_printer_name = "");
+	void		select_preset(const std::string& preset_name = "");
+	bool		may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = "");
 	wxSizer*	compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
 
 	void		update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible);
-	void		load_key_value(std::string opt_key, boost::any value);
+	void		load_key_value(const std::string& opt_key, const boost::any& value);
 	void		reload_compatible_printers_widget();
 
 	void		OnTreeSelChange(wxTreeEvent& event);
@@ -172,7 +172,7 @@ public:
 	void		on_back_to_initial_value();
 	void		on_back_to_sys_value();
 
-	PageShp		add_options_page(wxString title, std::string icon, bool is_extruder_pages = false);
+	PageShp		add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false);
 
 	virtual void	OnActivate(){}
 	virtual void	on_preset_loaded(){}
@@ -181,10 +181,10 @@ public:
 	void			load_initial_data();
 	void			update_dirty();
 	void			update_tab_ui();
-	void			load_config(DynamicPrintConfig config);
+	void			load_config(const DynamicPrintConfig& config);
 	virtual void	reload_config();
-	Field*			get_field(t_config_option_key opt_key, int opt_index = -1) const;
-	bool			set_value(t_config_option_key opt_key, boost::any value);
+	Field*			get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
+	bool			set_value(const t_config_option_key& opt_key, const boost::any& value);
 	wxSizer*		description_line_widget(wxWindow* parent, ogStaticText** StaticText);
 	bool			current_preset_is_dirty();
 	DynamicPrintConfig*	get_config() { return m_config; }
@@ -194,7 +194,7 @@ public:
 	}
 	std::vector<std::string>	get_dependent_tabs() { return m_reload_dependent_tabs; }
 
-	void			on_value_change(std::string opt_key, boost::any value);
+	void			on_value_change(const std::string& opt_key, const boost::any& value);
 
 protected:
 	void			on_presets_changed();
@@ -270,7 +270,7 @@ public:
 	std::string		m_chosen_name;
 	wxComboBox*		m_combo;
 
-	void			build(wxString title, std::string default_name, std::vector<std::string> &values);
+	void			build(const wxString& title, const std::string& default_name, std::vector<std::string> &values);
 	void			accept();
 	std::string		get_name() { return m_chosen_name; }
 };

From 64976c249d854572b9227e3283f35172e9d1a4a0 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 13 Apr 2018 12:49:12 +0200
Subject: [PATCH 110/118] Added @bubnikv's changes to BitmapCache

---
 xs/src/slic3r/GUI/BitmapCache.cpp | 186 +++++++++++++++++++-----------
 xs/src/slic3r/GUI/BitmapCache.hpp |   3 +-
 2 files changed, 121 insertions(+), 68 deletions(-)

diff --git a/xs/src/slic3r/GUI/BitmapCache.cpp b/xs/src/slic3r/GUI/BitmapCache.cpp
index acbcb5c73..93853458e 100644
--- a/xs/src/slic3r/GUI/BitmapCache.cpp
+++ b/xs/src/slic3r/GUI/BitmapCache.cpp
@@ -1,5 +1,14 @@
 #include "BitmapCache.hpp"
 
+#if ! defined(WIN32) && ! defined(__APPLE__)
+#define BROKEN_ALPHA
+#endif
+
+#ifdef BROKEN_ALPHA
+    #include <wx/mstream.h>
+    #include <wx/rawbmp.h>
+#endif /* BROKEN_ALPHA */
+
 namespace Slic3r { namespace GUI {
 
 void BitmapCache::clear()
@@ -8,19 +17,31 @@ void BitmapCache::clear()
         delete bitmap.second;
 }
 
+static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image)
+{
+#ifdef BROKEN_ALPHA
+    wxMemoryOutputStream stream;
+    image.SaveFile(stream, wxBITMAP_TYPE_PNG);
+    wxStreamBuffer *buf = stream.GetOutputStreamBuffer();
+    return wxBitmap::NewFromPNGData(buf->GetBufferStart(), buf->GetBufferSize());
+#else
+    return wxBitmap(std::move(image));
+#endif
+}
+
 wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height)
 {
-	wxBitmap *bitmap = nullptr;
-	auto 	  it     = m_map.find(bitmap_key);
-	if (it == m_map.end()) {
-		bitmap = new wxBitmap(width, height);
-    	m_map[bitmap_key] = bitmap;
-	} else {
-		bitmap = it->second;
-		if (bitmap->GetWidth() != width || bitmap->GetHeight() != height)
-			bitmap->Create(width, height);
-	}
-#if defined(__APPLE__) || defined(_MSC_VER)
+    wxBitmap *bitmap = nullptr;
+    auto      it     = m_map.find(bitmap_key);
+    if (it == m_map.end()) {
+        bitmap = new wxBitmap(width, height);
+        m_map[bitmap_key] = bitmap;
+    } else {
+        bitmap = it->second;
+        if (bitmap->GetWidth() != width || bitmap->GetHeight() != height)
+            bitmap->Create(width, height);
+    }
+#ifndef BROKEN_ALPHA
     bitmap->UseAlpha();
 #endif
     return bitmap;
@@ -28,77 +49,108 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_
 
 wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp)
 {
-	wxBitmap *bitmap = this->insert(bitmap_key, bmp.GetWidth(), bmp.GetHeight());
-
-    wxMemoryDC memDC;
-    memDC.SelectObject(*bitmap);
-    memDC.SetBackground(*wxTRANSPARENT_BRUSH);
-    memDC.Clear();
-    memDC.DrawBitmap(bmp, 0, 0, true);
-    memDC.SelectObject(wxNullBitmap);
-
-	return bitmap;
+    wxBitmap *bitmap = nullptr;
+    auto      it     = m_map.find(bitmap_key);
+    if (it == m_map.end()) {
+        bitmap = new wxBitmap(bmp);
+        m_map[bitmap_key] = bitmap;
+    } else {
+        bitmap = it->second;
+        *bitmap = bmp;
+    }
+    return bitmap;
 }
 
 wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2)
 {
-	wxBitmap *bitmap = this->insert(bitmap_key, bmp.GetWidth() + bmp2.GetWidth(), std::max(bmp.GetHeight(), bmp2.GetHeight()));
-
-    wxMemoryDC memDC;
-    memDC.SelectObject(*bitmap);
-    memDC.SetBackground(*wxTRANSPARENT_BRUSH);
-    memDC.Clear();
-    if (bmp.GetWidth() > 0)
-    	memDC.DrawBitmap(bmp,  0, 				0, true);
-    if (bmp2.GetWidth() > 0)
-	    memDC.DrawBitmap(bmp2, bmp.GetWidth(), 	0, true);
-    memDC.SelectObject(wxNullBitmap);
-
-	return bitmap;
+    // Copying the wxBitmaps is cheap as the bitmap's content is reference counted.
+    const wxBitmap bmps[2] = { bmp, bmp2 };
+    return this->insert(bitmap_key, bmps, bmps + 2);
 }
 
 wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3)
 {
-	wxBitmap *bitmap = this->insert(bitmap_key, bmp.GetWidth() + bmp2.GetWidth() + bmp3.GetWidth(), std::max(std::max(bmp.GetHeight(), bmp2.GetHeight()), bmp3.GetHeight()));
+    // Copying the wxBitmaps is cheap as the bitmap's content is reference counted.
+    const wxBitmap bmps[3] = { bmp, bmp2, bmp3 };
+    return this->insert(bitmap_key, bmps, bmps + 3);
+}
 
+wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end)
+{
+    size_t width  = 0;
+    size_t height = 0;
+    for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
+        width += bmp->GetWidth();
+        height = std::max<size_t>(height, bmp->GetHeight());
+    }
+
+#ifdef BROKEN_ALPHA
+
+    wxImage image(width, height);
+    image.InitAlpha();
+    // Fill in with a white color.
+    memset(image.GetData(), 0x0ff, width * height * 3);
+    // Fill in with full transparency.
+    memset(image.GetAlpha(),    0, width * height);
+    size_t x = 0;
+    for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
+        if (bmp->GetWidth() > 0) {
+            if (bmp->GetDepth() == 32) {
+                wxAlphaPixelData data(*const_cast<wxBitmap*>(bmp));
+                data.UseAlpha();
+                if (data) {
+                    for (int r = 0; r < bmp->GetHeight(); ++ r) {
+                        wxAlphaPixelData::Iterator src(data);
+                        src.Offset(data, 0, r);
+                        unsigned char *dst_pixels = image.GetData()  + (x + r * width) * 3;
+                        unsigned char *dst_alpha  = image.GetAlpha() +  x + r * width;
+                        for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) {
+                            *dst_pixels ++ = src.Red();
+                            *dst_pixels ++ = src.Green();
+                            *dst_pixels ++ = src.Blue();
+                            *dst_alpha  ++ = src.Alpha();
+                        }
+                    }
+                }
+            } else if (bmp->GetDepth() == 24) {
+                wxNativePixelData data(*const_cast<wxBitmap*>(bmp));
+                if (data) {
+                    for (int r = 0; r < bmp->GetHeight(); ++ r) {
+                        wxNativePixelData::Iterator src(data);
+                        src.Offset(data, 0, r);
+                        unsigned char *dst_pixels = image.GetData()  + (x + r * width) * 3;
+                        unsigned char *dst_alpha  = image.GetAlpha() +  x + r * width;
+                        for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) {
+                            *dst_pixels ++ = src.Red();
+                            *dst_pixels ++ = src.Green();
+                            *dst_pixels ++ = src.Blue();
+                            *dst_alpha  ++ = wxALPHA_OPAQUE;
+                        }
+                    }
+                }
+            }
+        }
+        x += bmp->GetWidth();
+    }
+    return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
+
+#else
+
+    wxBitmap *bitmap = this->insert(bitmap_key, width, height);
     wxMemoryDC memDC;
     memDC.SelectObject(*bitmap);
     memDC.SetBackground(*wxTRANSPARENT_BRUSH);
     memDC.Clear();
-    if (bmp.GetWidth() > 0)
-	    memDC.DrawBitmap(bmp,  0, 									0, true);
-    if (bmp2.GetWidth() > 0)
-    	memDC.DrawBitmap(bmp2, bmp.GetWidth(), 						0, true);
-    if (bmp3.GetWidth() > 0)
-	    memDC.DrawBitmap(bmp3, bmp.GetWidth() + bmp2.GetWidth(), 	0, true);
-    memDC.SelectObject(wxNullBitmap);
-
-	return bitmap;
-}
-
-wxBitmap* BitmapCache::insert(const std::string &bitmap_key, std::vector<wxBitmap> &bmps)
-{
-	size_t width  = 0;
-	size_t height = 0;
-	for (wxBitmap &bmp : bmps) {
-		width += bmp.GetWidth();
-		height = std::max<size_t>(height, bmp.GetHeight());
-	}
-	wxBitmap *bitmap = this->insert(bitmap_key, width, height);
-
-    wxMemoryDC memDC;
-    memDC.SelectObject(*bitmap);
-	memDC.SetBackground(*wxTRANSPARENT_BRUSH);
-    memDC.Clear();
     size_t x = 0;
-	for (wxBitmap &bmp : bmps) {
-	    if (bmp.GetWidth() > 0)
-		    memDC.DrawBitmap(bmp, x, 0, true);
-		x += bmp.GetWidth();
-	}
+    for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
+        if (bmp->GetWidth() > 0)
+            memDC.DrawBitmap(*bmp, x, 0, true);
+        x += bmp->GetWidth();
+    }
     memDC.SelectObject(wxNullBitmap);
+    return bitmap;
 
-	return bitmap;
+#endif
 }
 
 wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency)
@@ -113,7 +165,7 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi
         *imgdata ++ = b;
         *imgalpha ++ = transparency;
     }
-    return wxBitmap(std::move(image));
+    return wxImage_to_wxBitmap_with_alpha(std::move(image));
 }
 
 } // namespace GUI
diff --git a/xs/src/slic3r/GUI/BitmapCache.hpp b/xs/src/slic3r/GUI/BitmapCache.hpp
index 0cf9d8acf..bec9a7ad2 100644
--- a/xs/src/slic3r/GUI/BitmapCache.hpp
+++ b/xs/src/slic3r/GUI/BitmapCache.hpp
@@ -27,7 +27,8 @@ public:
 	wxBitmap* 		insert(const std::string &name, const wxBitmap &bmp);
 	wxBitmap* 		insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2);
 	wxBitmap* 		insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
-	wxBitmap* 		insert(const std::string &name, std::vector<wxBitmap> &bmps);
+	wxBitmap* 		insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
+	wxBitmap* 		insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
 
 	static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
 	static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }

From abe6e8a783b62fc402aaea783906cb52a6381478 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 13 Apr 2018 13:43:53 +0200
Subject: [PATCH 111/118] Bugfix: legacy config options were not properly
 processed

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

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 4012b91f8..dcece7a9b 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -644,7 +644,7 @@ std::string Print::validate() const
         unsigned int total_extruders_count = this->config.nozzle_diameter.size();
         for (const auto& extruder_idx : extruders)
             if ( extruder_idx >= total_extruders_count )
-                return "One or more object have assigned an extruder that the printer does not have.";
+                return "One or more object were assigned an extruder that the printer does not have.";
 
         for (PrintObject *object : this->objects) {
             if ((object->config.support_material_extruder == -1 || object->config.support_material_interface_extruder == -1) &&
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 66576c9f1..1e7e0bacc 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1944,8 +1944,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
         "standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
         "start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start", 
         "seal_position", "vibration_limit", "bed_size", "octoprint_host",
-        "print_center", "g0", "threads", "pressure_advance" 
+        "print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe"
     };
+
     if (ignore.find(opt_key) != ignore.end()) {
         opt_key = "";
         return;
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index be64d35bd..967a87310 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -154,6 +154,13 @@ public:
 
     // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned.
     std::string         validate();
+
+    // Verify whether the opt_key has not been obsoleted or renamed.
+    // Both opt_key and value may be modified by handle_legacy().
+    // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
+    // handle_legacy() is called internally by set_deserialize().
+    void                handle_legacy(t_config_option_key &opt_key, std::string &value) const override
+        { PrintConfigDef::handle_legacy(opt_key, value); }
 };
 
 template<typename CONFIG>

From 68c3749696bcad48f0801129dd10a6f190a02a55 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 13 Apr 2018 13:46:31 +0200
Subject: [PATCH 112/118] Gyroid infill - automatic discretization steps and
 refactoring

---
 xs/src/libslic3r/Fill/FillGyroid.cpp | 162 ++++++++++++++++++---------
 1 file changed, 106 insertions(+), 56 deletions(-)

diff --git a/xs/src/libslic3r/Fill/FillGyroid.cpp b/xs/src/libslic3r/Fill/FillGyroid.cpp
index a3920caab..d66e63a60 100644
--- a/xs/src/libslic3r/Fill/FillGyroid.cpp
+++ b/xs/src/libslic3r/Fill/FillGyroid.cpp
@@ -9,77 +9,125 @@
 
 namespace Slic3r {
 
-static inline Polyline make_wave_vertical(
-    double width, double height, double x0,
-    double segmentSize, double scaleFactor,
-    double z_cos, double z_sin, bool flip)
-{
-    Polyline polyline;
-    polyline.points.emplace_back(Point(coord_t(clamp(0., width, x0) * scaleFactor), 0));
-    double phase_offset_sin = (z_cos < 0 ? M_PI : 0) + M_PI;
-    double phase_offset_cos = (z_cos < 0 ? M_PI : 0) + M_PI + (flip ? M_PI : 0.);
-    for (double y = 0.; y < height + segmentSize; y += segmentSize) {
-        y = std::min(y, height);
-        double a   = sin(y + phase_offset_sin);
+
+static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip) {
+    if (vertical) {
+        double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
+        double a   = sin(x + phase_offset);
         double b   = - z_cos;
-        double res = z_sin * cos(y + phase_offset_cos);
+        double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
         double r   = sqrt(sqr(a) + sqr(b));
-        double x   = clamp(0., width, asin(a/r) + asin(res/r) + M_PI + x0);
-        polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
+        return asin(a/r) + asin(res/r) + M_PI;
     }
-    if (flip)
-        std::reverse(polyline.points.begin(), polyline.points.end());
+    else {
+        double phase_offset = z_sin < 0 ? M_PI : 0.;
+        double a   = cos(x + phase_offset);
+        double b   = - z_sin;
+        double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
+        double r   = sqrt(sqr(a) + sqr(b));
+        return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
+    }
+}
+
+
+static inline Polyline make_wave(
+    const std::vector<Pointf>& one_period, double width, double height, double offset, double scaleFactor,
+    double z_cos, double z_sin, bool vertical)
+{
+    std::vector<Pointf> points = one_period;
+    double period = points.back().x;
+    points.pop_back();
+    int n = points.size();
+    do {
+        points.emplace_back(Pointf(points[points.size()-n].x + period, points[points.size()-n].y));
+    } while (points.back().x < width);
+    points.back().x = width;
+
+    // and construct the final polyline to return:
+    Polyline polyline;
+    for (auto& point : points) {
+        point.y += offset;
+        point.y = clamp(0., height, double(point.y));
+        if (vertical)
+            std::swap(point.x, point.y);
+        polyline.points.emplace_back(convert_to<Point>(point * scaleFactor));
+    }
+
     return polyline;
 }
 
-static inline Polyline make_wave_horizontal(
-    double width, double height, double y0, 
-    double segmentSize, double scaleFactor,
-    double z_cos, double z_sin, bool flip)
-{
-    Polyline polyline;
-    polyline.points.emplace_back(Point(0, coord_t(clamp(0., height, y0) * scaleFactor)));
-    double phase_offset_sin = (z_sin < 0 ? M_PI : 0) + (flip ? 0 : M_PI);
-    double phase_offset_cos = z_sin < 0 ? M_PI : 0.;
-    for (double x = 0.; x < width + segmentSize; x += segmentSize) {
-        x = std::min(x, width);
-        double a   = cos(x + phase_offset_cos);
-        double b   = - z_sin;
-        double res = z_cos * sin(x + phase_offset_sin);
-        double r   = sqrt(sqr(a) + sqr(b));
-        double y   = clamp(0., height, asin(a/r) + asin(res/r) + 0.5 * M_PI + y0);
-        polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
+
+static std::vector<Pointf> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip) {
+    std::vector<Pointf> points;
+    double dx = M_PI_4; // very coarse spacing to begin with
+    double limit = std::min(2*M_PI, width);
+    for (double x = 0.; x < limit + EPSILON; x += dx) {  // so the last point is there too
+        x = std::min(x, limit);
+        points.emplace_back(Pointf(x,f(x, z_sin,z_cos, vertical, flip)));
     }
-    if (flip)
-        std::reverse(polyline.points.begin(), polyline.points.end());
-    return polyline;
+
+    // now we will check all internal points and in case some are too far from the line connecting its neighbours,
+    // we will add one more point on each side:
+    const double tolerance = .1;
+    for (unsigned int i=1;i<points.size()-1;++i) {
+        auto& lp = points[i-1]; // left point
+        auto& tp = points[i];   // this point
+        auto& rp = points[i+1]; // right point
+        // calculate distance of the point to the line:
+        double dist_mm = unscale(scaleFactor * std::abs( (rp.y - lp.y)*tp.x + (lp.x - rp.x)*tp.y + (rp.x*lp.y - rp.y*lp.x) ) / std::hypot((rp.y - lp.y),(lp.x - rp.x)));
+
+        if (dist_mm > tolerance) {                               // if the difference from straight line is more than this
+            double x = 0.5f * (points[i-1].x + points[i].x);
+            points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip)));
+            x = 0.5f * (points[i+1].x + points[i].x);
+            points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip)));
+            std::sort(points.begin(), points.end());            // we added the points to the end, but need them all in order
+            --i;                                                // decrement i so we also check the first newly added point
+        }
+    }
+    return points;
 }
 
+
 static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
 {
-    double scaleFactor = scale_(line_spacing) / density_adjusted;
-    double segmentSize = 0.5 * density_adjusted;
+    const double scaleFactor = scale_(line_spacing) / density_adjusted;
  //scale factor for 5% : 8 712 388
  // 1z = 10^-6 mm ?
-    double z     = gridZ / scaleFactor;
-    double z_sin = sin(z);
-    double z_cos = cos(z);
-    Polylines result;
-    if (abs(z_sin) <= abs(z_cos)) {
-        // Vertical wave
-        double x0 = M_PI * (int)((- 0.5 * M_PI) / M_PI - 1.);
-        bool   flip          = ((int)(x0 / M_PI + 1.) & 1) != 0;
-        for (; x0 < width - 0.5 * M_PI; x0 += M_PI, flip = ! flip)
-            result.emplace_back(make_wave_vertical(width, height, x0, segmentSize, scaleFactor, z_cos, z_sin, flip));
-    } else {
-        // Horizontal wave
-        bool flip = true;
-        for (double y0 = 0.; y0 < height; y0 += M_PI, flip = !flip)
-            result.emplace_back(make_wave_horizontal(width, height, y0, segmentSize, scaleFactor, z_cos, z_sin, flip));
+    const double z     = gridZ / scaleFactor;
+    const double z_sin = sin(z);
+    const double z_cos = cos(z);
+
+    bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
+    double lower_bound = 0.;
+    double upper_bound = height;
+    bool flip = true;
+    if (vertical) {
+        flip = false;
+        lower_bound = -M_PI;
+        upper_bound = width - M_PI_2;
+        std::swap(width,height);
     }
+
+    std::vector<Pointf> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
+    Polylines result;
+
+    for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI)           // creates odd polylines
+            result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
+
+    flip = !flip;                                                                   // even polylines are a bit shifted
+    one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
+    for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI)    // creates even polylines
+            result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
+
     return result;
 }
 
+
+
+
+
+
 void FillGyroid::_fill_surface_single(
     const FillParams                &params, 
     unsigned int                     thickness_layers,
@@ -90,13 +138,15 @@ void FillGyroid::_fill_surface_single(
     // no rotation is supported for this infill pattern (yet)
     BoundingBox bb = expolygon.contour.bounding_box();
     // Density adjusted to have a good %of weight.
-    double      density_adjusted = params.density * 1.75;
+    double      density_adjusted = std::max(0., params.density * 2.);
+
+
     // Distance between the gyroid waves in scaled coordinates.
     coord_t     distance = coord_t(scale_(this->spacing) / density_adjusted);
 
     // align bounding box to a multiple of our grid module
     bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance)));
-    
+
     // generate pattern
     Polylines   polylines = make_gyroid_waves(
         scale_(this->z),

From 359e7e4d32225d485c36c4733ba9f3847d5c80b5 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 13 Apr 2018 13:59:36 +0200
Subject: [PATCH 113/118] Scale down meshes of too big objects after loading
 them

---
 lib/Slic3r/GUI/Plater.pm | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index c74b2d32c..ce37a1651 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -738,7 +738,13 @@ sub load_model_objects {
             # if the object is too large (more than 5 times the bed), scale it down
             my $size = $o->bounding_box->size;
             my $ratio = max($size->x / unscale($bed_size->x), $size->y / unscale($bed_size->y));
-            if ($ratio > 5) {
+            if ($ratio > 10000) {
+                # the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
+                # so scale down the mesh
+                $o->scale_xyz(Slic3r::Pointf3->new(1/$ratio, 1/$ratio, 1/$ratio));
+                $scaled_down = 1;
+            }
+            elsif ($ratio > 5) {
                 $_->set_scaling_factor(1/$ratio) for @{$o->instances};
                 $scaled_down = 1;
             }

From 659eb3412b132350bafad6a9c8fd74feaa5560fd Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Fri, 13 Apr 2018 14:40:42 +0200
Subject: [PATCH 114/118] Fixed value of z in cut dialog when object is scaled

---
 lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 7f930c1d0..7b5752cd2 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -60,7 +60,7 @@ sub new {
         label       => 'Z',
         default     => $self->{cut_options}{z},
         min         => 0,
-        max         => $self->{model_object}->bounding_box->size->z * $self->{model_object}->instances->[0]->scaling_factor,
+        max         => $self->{model_object}->bounding_box->size->z,
         full_width  => 1,
     ));
     {
@@ -244,7 +244,7 @@ sub _update {
             $self->{canvas}->reset_objects;
             $self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
             $self->{canvas}->SetCuttingPlane(
-                $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor,
+                $self->{cut_options}{z},
                 [@expolygons],
             );
             $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config});

From 5d363c1bb9ac2f4a2d4fae8faea3bf473ac55f34 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 13 Apr 2018 17:25:36 +0200
Subject: [PATCH 115/118] Removed printf debugging output

---
 xs/src/libslic3r/Model.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index bda49421d..aaea863e8 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -863,7 +863,6 @@ void ModelObject::cut(coordf_t z, Model* model) const
             lower->add_volume(*volume);
         } else {
             TriangleMesh upper_mesh, lower_mesh;
-            printf("Cutting mesh patch\n");
             TriangleMeshSlicer tms(&volume->mesh);
             tms.cut(z, &upper_mesh, &lower_mesh);
 

From 215c2082d3bb011f5ba95cd03b8614abafb488e8 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 13 Apr 2018 18:22:06 +0200
Subject: [PATCH 116/118] Decorated "bed_shape" and "compatible_printers"
 labels

---
 xs/src/slic3r/GUI/OptionsGroup.cpp |  7 +++-
 xs/src/slic3r/GUI/OptionsGroup.hpp |  2 +-
 xs/src/slic3r/GUI/Tab.cpp          | 62 ++++++++++++++++++++++--------
 xs/src/slic3r/GUI/Tab.hpp          |  2 +
 4 files changed, 56 insertions(+), 17 deletions(-)

diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index 8e2268965..168ffcdc9 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -97,7 +97,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
     return field;
 }
 
-void OptionsGroup::append_line(const Line& line) {
+void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/* = nullptr*/) {
 //!    if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){
 	if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){
 		if (line.sizer != nullptr) {
@@ -150,6 +150,7 @@ void OptionsGroup::append_line(const Line& line) {
 	if (line.widget != nullptr) {
 		auto wgt = line.widget(parent());
 		grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5);
+		if (colored_Label != nullptr) *colored_Label = label;
 		return;
 	}
 	
@@ -396,6 +397,10 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 		ret = static_cast<wxString>(config.opt_string(opt_key));
 		break;
 	case coStrings:
+		if (opt_key.compare("compatible_printers") == 0){
+			ret = config.option<ConfigOptionStrings>(opt_key)->values;
+			break;
+		}
 		if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
 			ret = text_value;
 		else if (opt->gui_flags.compare("serialized") == 0){
diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp
index b5846f24d..dd6d48f46 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.hpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.hpp
@@ -93,7 +93,7 @@ public:
     /// but defining it as const means a lot of const_casts to deal with wx functions.
     inline wxWindow* parent() const { return m_parent; }
 
-    void		append_line(const Line& line);
+	void		append_line(const Line& line, wxStaticText** colored_Label = nullptr);
     Line		create_single_option_line(const Option& option) const;
     void		append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); }
 
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index a9faf4c59..4f6580f24 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -326,6 +326,7 @@ void Tab::update_changed_ui()
 		m_dirty_options = dirty_options;
 	}
 
+	Freeze();
 	//update options "decoration"
 	for (const auto opt_key : m_full_options_list)
 	{
@@ -347,6 +348,14 @@ void Tab::update_changed_ui()
 			is_modified_value = false;
 			icon = "bullet_white.png";
 		}
+		if (opt_key == "bed_shape" || opt_key == "compatible_printers") {
+			if (m_colored_Label != nullptr)	{
+				m_colored_Label->SetForegroundColour(color);
+				m_colored_Label->Refresh(true);
+			}
+			continue;
+		}
+
 		Field* field = get_field(opt_key);
 		if (field == nullptr) continue;
 		field->m_is_nonsys_value = is_nonsys_value;
@@ -358,6 +367,7 @@ void Tab::update_changed_ui()
 			field->m_Label->Refresh(true);
 		}
 	}
+	Thaw();
 
 	wxTheApp->CallAfter([this]() {
 		update_changed_tree_ui();
@@ -419,13 +429,20 @@ void Tab::update_sys_ui_after_sel_preset()
 	m_sys_options.resize(0);
 }
 
+void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page)
+{
+	if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
+		sys_page = false;
+	if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
+		modified_page = true;
+}
+
 void Tab::update_changed_tree_ui()
 {
 	auto cur_item = m_treectrl->GetFirstVisibleItem();
 	auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
 	while (cur_item){
 		auto title = m_treectrl->GetItemText(cur_item);
-		int i=0;
 		for (auto page : m_pages)
 		{
 			if (page->title() != title)
@@ -435,23 +452,20 @@ void Tab::update_changed_tree_ui()
 			if (title == _("General")){
 				std::initializer_list<const char*> optional_keys{ "extruders_count", "bed_shape" };
 				for (auto &opt_key : optional_keys) {
-					if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
-						sys_page = false;
-					if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
-						modified_page = true;
+					get_sys_and_mod_flags(opt_key, sys_page, modified_page);
 				}
 			}
+			if (title == _("Dependencies")){
+				get_sys_and_mod_flags("compatible_printers", sys_page, modified_page);
+			}
 			for (auto group : page->m_optgroups)
 			{
-				for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
-					const std::string& opt_key = it->first;
-					if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
-						sys_page = false;
-					if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
-						modified_page = true;
-				}
 				if (!sys_page && modified_page)
 					break;
+				for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
+					const std::string& opt_key = it->first;
+					get_sys_and_mod_flags(opt_key, sys_page, modified_page);
+				}
 			}
 			if (sys_page)
 				m_treectrl->SetItemTextColour(cur_item, get_sys_label_clr());
@@ -500,6 +514,14 @@ void Tab::on_back_to_initial_value()
 					if (find(m_dirty_options.begin(), m_dirty_options.end(), "bed_shape") != m_dirty_options.end())
 						group->back_to_initial_value("bed_shape");
 				}
+				if (group->title == _("Profile dependencies")){
+					if (find(m_dirty_options.begin(), m_dirty_options.end(), "compatible_printers") != m_dirty_options.end())
+						group->back_to_initial_value("compatible_printers");
+
+					bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
+					m_compatible_printers_checkbox->SetValue(is_empty);
+					is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
+				}
 				for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
 					const std::string& opt_key = it->first;
 					if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
@@ -527,6 +549,14 @@ void Tab::on_back_to_sys_value()
 					if (find(m_sys_options.begin(), m_sys_options.end(), "bed_shape") == m_sys_options.end())
 						group->back_to_sys_value("bed_shape");
 				}
+				if (group->title == _("Profile dependencies")){
+					if (find(m_sys_options.begin(), m_sys_options.end(), "compatible_printers") == m_sys_options.end())
+						group->back_to_sys_value("compatible_printers");
+
+					bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
+					m_compatible_printers_checkbox->SetValue(is_empty);
+					is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
+				}
 				for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
 					const std::string& opt_key = it->first;
 					if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
@@ -905,7 +935,7 @@ void TabPrint::build()
 		line.widget = [this](wxWindow* parent){
 			return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
 		};
-		optgroup->append_line(line);
+		optgroup->append_line(line, &m_colored_Label);
 
 		option = optgroup->get_option("compatible_printers_condition");
 		option.opt.full_width = true;
@@ -1279,7 +1309,7 @@ void TabFilament::build()
 		line.widget = [this](wxWindow* parent){
 			return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
 		};
-		optgroup->append_line(line);
+		optgroup->append_line(line, &m_colored_Label);
 
 		option = optgroup->get_option("compatible_printers_condition");
 		option.opt.full_width = true;
@@ -1376,7 +1406,7 @@ void TabPrinter::build()
 
 			return sizer;
 		};
-		optgroup->append_line(line);
+		optgroup->append_line(line, &m_colored_Label);
         optgroup->append_single_option_line("max_print_height");
         optgroup->append_single_option_line("z_offset");
 
@@ -2114,6 +2144,7 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
 		if ((*checkbox)->GetValue())
 			load_key_value("compatible_printers", std::vector<std::string> {});
 		get_field("compatible_printers_condition")->toggle((*checkbox)->GetValue());
+		update_changed_ui();
 	}) );
 
 	(*btn)->Bind(wxEVT_BUTTON, ([this, parent, checkbox, btn](wxCommandEvent e)
@@ -2156,6 +2187,7 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
 			}
 			// All printers have been made compatible with this preset.
 			load_key_value("compatible_printers", value);
+			update_changed_ui();
 		}
 	}));
 	return sizer; 
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 9465f86b4..c69f4285d 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -126,6 +126,7 @@ public:
 	DynamicPrintConfig*	m_config;
 	std::string			m_nonsys_btn_icon;
 	ogStaticText*		m_parent_preset_description_line;
+	wxStaticText*		m_colored_Label = nullptr;
 
 public:
 	Tab() {}
@@ -166,6 +167,7 @@ public:
 	void		update_changed_ui();
 	void		update_full_options_list();
 	void		update_sys_ui_after_sel_preset();
+	void		get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page);
 	void		update_changed_tree_ui();
 	void		update_undo_buttons();
 

From b881ae936f7e943b2d45353fffd1a2a1291317da Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 13 Apr 2018 18:36:52 +0200
Subject: [PATCH 117/118] Updated preset from Jindra.

---
 resources/profiles/PrusaResearch.ini | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index fa4d48cda..de23bf0ad 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -597,6 +597,7 @@ temperature = 255
 
 [filament:*FLEX*]
 inherits = *common*
+bed_temperature = 50
 bridge_fan_speed = 100
 compatible_printers_condition = nozzle_diameter[0]>0.35 and num_extruders==1
 cooling = 0
@@ -947,6 +948,7 @@ variable_layer_height = 0
 
 [printer:Original Prusa i3 MK2]
 inherits = *common*
+default_print_profile = 0.15mm OPTIMAL
 
 [printer:Original Prusa i3 MK2 0.25 nozzle]
 inherits = *common*
@@ -965,6 +967,7 @@ max_layer_height = 0.35
 min_layer_height = 0.1
 nozzle_diameter = 0.6
 printer_variant = 0.6
+default_print_profile = 0.20mm NORMAL 0.6 nozzle
 
 [printer:Original Prusa i3 MK2 MM Single Mode]
 inherits = *mm-single*
@@ -973,6 +976,7 @@ inherits = *mm-single*
 inherits = *mm-single*
 nozzle_diameter = 0.6
 printer_variant = 0.6
+default_print_profile = 0.20mm NORMAL 0.6 nozzle
 
 [printer:Original Prusa i3 MK2 MultiMaterial]
 inherits = *mm-multi*

From 2726267748b7ff23665794db121db2484fbc3060 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 16 Apr 2018 11:47:35 +0200
Subject: [PATCH 118/118] Bugfix: validation of equal layering rejected even
 some valid configurations

---
 xs/src/libslic3r/Print.cpp | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index dcece7a9b..c19c97fae 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -598,6 +598,12 @@ std::string Print::validate() const
         if (! this->config.use_relative_e_distances)
             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
+        for (const auto* object : objects)
+            if (object->layer_height_profile.size() > most_layered_object->layer_height_profile.size())
+                most_layered_object = object;
+
         for (PrintObject *object : this->objects) {
             SlicingParameters slicing_params = object->slicing_parameters();
             if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
@@ -614,12 +620,15 @@ std::string Print::validate() const
             object->layer_height_profile_valid = was_layer_height_profile_valid;
 
             if ( this->config.variable_layer_height ) {
-                PrintObject* first_object = this->objects.front();
                 int i = 0;
-                while ( i < first_object->layer_height_profile.size() && i < object->layer_height_profile.size() ) {
-                    if (std::abs(first_object->layer_height_profile[i] - object->layer_height_profile[i]) > EPSILON )
+                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;
                 }
             }