From 8e5ba5ccc5fe9720fde7441aea3654f538539a76 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 15 Jan 2020 16:20:16 +0100
Subject: [PATCH] Added synonyms to some PrintSteps:

psWipeTower now equals to new psToolOrdering indicating that
the ToolOrdering has been calculated (only if non-sequential mode is active).

psBrim now equals to new psExtrusionPaths
psExtrusionPaths shall be the last step before psWipeTower, indicating
that all the printing extrusions are calculated for the G-code preview
slider to edit the custom per print_z color changes, tool changes etc.
---
 src/libslic3r/GCode.cpp              |  5 ++--
 src/libslic3r/GCode/ToolOrdering.cpp |  3 ++
 src/libslic3r/GCode/ToolOrdering.hpp |  7 ++++-
 src/libslic3r/Print.cpp              | 44 ++++++++++++++++++++++++++--
 src/libslic3r/Print.hpp              | 19 ++++++++++--
 5 files changed, 68 insertions(+), 10 deletions(-)

diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 870749867..7627f581d 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1222,9 +1222,8 @@ void GCode::_do_export(Print& print, FILE* file)
     } else {
 		// Find tool ordering for all the objects at once, and the initial extruder ID.
         // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
-		tool_ordering = print.wipe_tower_data().tool_ordering.empty() ?
-            ToolOrdering(print, initial_extruder_id, false) :
-            print.wipe_tower_data().tool_ordering;
+		tool_ordering = print.tool_ordering();
+		tool_ordering.assign_custom_gcodes(print);
         has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
         initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ?
             // The priming towers will be skipped.
diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp
index a76f0fafb..2628e1926 100644
--- a/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/src/libslic3r/GCode/ToolOrdering.cpp
@@ -460,6 +460,9 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
 // If multiple events are planned over a span of a single layer, use the last one.
 void ToolOrdering::assign_custom_gcodes(const Print &print)
 {
+	// Only valid for non-sequential print.
+	assert(! print.config().complete_objects.value);
+
 	const std::vector<Model::CustomGCode>	&custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
 	if (custom_gcode_per_print_z.empty())
 		return;
diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp
index a9d5a98e7..08dd72656 100644
--- a/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/src/libslic3r/GCode/ToolOrdering.hpp
@@ -143,6 +143,12 @@ public:
 
     void 				clear() { m_layer_tools.clear(); }
 
+    // Only valid for non-sequential print:
+	// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
+	// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
+	// If multiple events are planned over a span of a single layer, use the last one.
+	void 				assign_custom_gcodes(const Print &print);
+
     // Get the first extruder printing, including the extruder priming areas, returns -1 if there is no layer printed.
     unsigned int   		first_extruder() const { return m_first_printing_extruder; }
 
@@ -170,7 +176,6 @@ private:
     void				reorder_extruders(unsigned int last_extruder_id);
     void 				fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
     void 				collect_extruder_statistics(bool prime_multi_material);
-	void 				assign_custom_gcodes(const Print &print);
 
     std::vector<LayerTools>    m_layer_tools;
     // First printing extruder, including the multi-material priming sequence.
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index ed8817882..babdd5aa4 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -478,7 +478,7 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
 
 // Compare just the layer ranges and their layer heights, not the associated configs.
 // Ignore the layer heights if check_layer_heights is false.
-bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
+static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
 {
     if (lr1.size() != lr2.size())
         return false;
@@ -493,6 +493,37 @@ bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_c
     return true;
 }
 
+// Returns true if va == vb when all CustomGCode items that are not ExtruderChangeCode are ignored.
+static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<Model::CustomGCode> &va, const std::vector<Model::CustomGCode> &vb)
+{
+	auto it_a = va.begin();
+	auto it_b = vb.begin();
+	while (it_a != va.end() && it_b != vb.end()) {
+		if (it_a != va.end() && it_a->gcode != ExtruderChangeCode) {
+			// Skip any CustomGCode items, which are not tool changes.
+			++ it_a;
+			continue;
+		}
+		if (it_b != vb.end() && it_b->gcode != ExtruderChangeCode) {
+			// Skip any CustomGCode items, which are not tool changes.
+			++ it_b;
+			continue;
+		}
+		if (it_a == va.end() || it_b == vb.end())
+			// va or vb contains more Tool Changes than the other.
+			return true;
+		assert(it_a->gcode == ExtruderChangeCode);
+		assert(it_b->gcode == ExtruderChangeCode);
+		if (*it_a != *it_b)
+			// The two Tool Changes differ.
+			return true;
+		++ it_a;
+		++ it_b;
+	}
+	// There is no change in custom Tool Changes.
+	return false;
+}
+
 // Collect diffs of configuration values at various containers,
 // resolve the filament rectract overrides of extruder retract values.
 void Print::config_diffs(
@@ -692,8 +723,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
 			model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
     } else {
         if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
-	        // If custom gcode per layer height was changed, we should stop background processing.
-            update_apply_status(this->invalidate_steps({ psWipeTower, psGCodeExport }));
+            update_apply_status(custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z, model.custom_gcode_per_print_z) ?
+            	// The Tool Ordering and the Wipe Tower are no more valid.
+            	this->invalidate_steps({ psWipeTower, psGCodeExport }) :
+            	// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
+            	this->invalidate_step(psGCodeExport));
             m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
         }
         if (model_object_list_equal(m_model, model)) {
@@ -1521,9 +1555,13 @@ void Print::process()
         obj->generate_support_material();
     if (this->set_started(psWipeTower)) {
         m_wipe_tower_data.clear();
+        m_tool_ordering.clear();
         if (this->has_wipe_tower()) {
             //this->set_status(95, L("Generating wipe tower"));
             this->_make_wipe_tower();
+        } else if (! this->config().complete_objects.value) {
+        	// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
+        	m_tool_ordering = ToolOrdering(*this, -1, false);
         }
         this->set_done(psWipeTower);
     }
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 42f8d761e..4a4ed3964 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -27,8 +27,19 @@ class GCodePreviewData;
 
 // Print step IDs for keeping track of the print state.
 enum PrintStep {
-    psSkirt, psBrim, psWipeTower, psGCodeExport, psCount,
+    psSkirt, 
+    psBrim,
+    // Synonym for the last step before the Wipe Tower / Tool Ordering, for the G-code preview slider to understand that 
+    // all the extrusions are there for the layer slider to add color changes etc.
+    psExtrusionPaths = psBrim,
+    psWipeTower,
+    // psToolOrdering is a synonym to psWipeTower, as the Wipe Tower calculates and modifies the ToolOrdering,
+    // while if printing without the Wipe Tower, the ToolOrdering is calculated as well.
+    psToolOrdering = psWipeTower,
+    psGCodeExport,
+    psCount,
 };
+
 enum PrintObjectStep {
     posSlice, posPerimeters, posPrepareInfill,
     posInfill, posSupportMaterial, posCount,
@@ -231,6 +242,7 @@ private:
 
 struct WipeTowerData
 {
+	WipeTowerData(ToolOrdering &tool_ordering) : tool_ordering(tool_ordering) { clear(); }
     // Following section will be consumed by the GCodeGenerator.
     // Tool ordering of a non-sequential print has to be known to calculate the wipe tower.
     // Cache it here, so it does not need to be recalculated during the G-code generation.
@@ -247,7 +259,6 @@ struct WipeTowerData
     float                                                 brim_width;
 
     void clear() {
-        tool_ordering.clear();
         priming.reset(nullptr);
         tool_changes.clear();
         final_purge.reset(nullptr);
@@ -377,6 +388,7 @@ public:
     // Wipe tower support.
     bool                        has_wipe_tower() const;
     const WipeTowerData&        wipe_tower_data(size_t extruders_cnt = 0, double first_layer_height = 0., double nozzle_diameter = 0.) const;
+    const ToolOrdering& 		tool_ordering() const { return m_tool_ordering; }
 
 	std::string                 output_filename(const std::string &filename_base = std::string()) const override;
 
@@ -420,7 +432,8 @@ private:
     ExtrusionEntityCollection               m_brim;
 
     // Following section will be consumed by the GCodeGenerator.
-    WipeTowerData                           m_wipe_tower_data;
+    ToolOrdering 							m_tool_ordering;
+    WipeTowerData                           m_wipe_tower_data {m_tool_ordering};
 
     // Estimated print time, filament consumed.
     PrintStatistics                         m_print_statistics;