Index: branches/PerformanceComparison/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs	(revision 12803)
+++ branches/PerformanceComparison/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs	(revision 12804)
@@ -42,4 +42,6 @@
     }
 
+    private double[] levels;
+
     private bool suppressUpdates;
     private readonly IndexedDataTable<double> combinedDataTable;
@@ -167,7 +169,12 @@
         var table = (string)dataTableComboBox.SelectedItem;
         if (string.IsNullOrEmpty(table)) return;
-        var maximum = Content.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Max(y => y.Item2)).Max();
-        var minimum = Content.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Min(y => y.Item2)).Min();
-        var levels = Enumerable.Range(0, 51).Select(x => maximum - (x / 50.0) * (maximum - minimum)).ToArray();
+        if (levels == null) {
+          var worst = Content.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Max(y => y.Item2)).First();
+          var best = Content.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Min(y => y.Item2)).Last();
+          levels = Enumerable.Range(0, 51).Select(x => worst + (x / 50.0) * (best - worst)).ToArray();
+          suppressTargetsEvents = true;
+          targetsTextBox.Text = string.Join(" ; ", levels);
+          suppressTargetsEvents = false;
+        }
         var selectedGroup = (string)groupComboBox.SelectedItem;
         if (string.IsNullOrEmpty(selectedGroup)) return;
@@ -184,25 +191,23 @@
             var resultsTable = (IndexedDataTable<double>)run.Results[table];
             xAxisTitles.Add(resultsTable.VisualProperties.XAxisTitle);
-            var graph = new LinkedList<Tuple<double, double>>(resultsTable.Rows.First().Values);
-            var current = graph.First.Next;
-            // prune convergence graph to obtain first hit times only
-            while (current != null) {
-              if (current.Value.Item2.IsAlmost(current.Previous.Value.Item2)) {
-                var h = current;
-                current = current.Previous;
-                graph.Remove(h);
-              }
-              current = current.Next;
-            }
+            var values = resultsTable.Rows.First().Values;
+            var maximization = values.First().Item2 < values.Last().Item2;
             var i = 0;
-            current = graph.First;
-            while (i < levels.Length && current != null) {
-              if (current.Value.Item2 < levels[i]) {
-                if (hits.ContainsKey(current.Value.Item1))
-                  hits[current.Value.Item1]++;
-                else hits[current.Value.Item1] = 1;
+            var j = 0;
+            var current = values[j];
+            var prev = Tuple.Create(-1.0, double.NaN);
+            while (i < levels.Length) {
+              if ((double.IsNaN(prev.Item2) || prev.Item2 != current.Item2)
+                  && (maximization && current.Item2 >= levels[i]
+                  || !maximization && current.Item2 <= levels[i])) {
+                if (hits.ContainsKey(current.Item1))
+                  hits[current.Item1]++;
+                else hits[current.Item1] = 1;
                 i++;
               } else {
-                current = current.Next;
+                j++;
+                if (j >= values.Count) break;
+                prev = current;
+                current = values[j];
               }
             }
@@ -266,4 +271,28 @@
       combinedDataTable.VisualProperties.XAxisLogScale = logScalingCheckBox.Checked;
     }
+
+    private bool suppressTargetsEvents;
+    private void targetsTextBox_Validating(object sender, CancelEventArgs e) {
+      if (suppressTargetsEvents) return;
+      var targetStrings = targetsTextBox.Text.Split(new[] { ';', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+      var targetList = new List<double>();
+      foreach (var ts in targetStrings) {
+        double t;
+        if (!double.TryParse(ts, out t)) {
+          errorProvider.SetError(targetsTextBox, "Not all targets can be parsed: " + ts);
+          e.Cancel = true;
+          return;
+        }
+        targetList.Add(t);
+      }
+      if (targetList.Count == 0) {
+        errorProvider.SetError(targetsTextBox, "Give at least one target value!");
+        e.Cancel = true;
+        return;
+      }
+      e.Cancel = false;
+      errorProvider.SetError(targetsTextBox, null);
+      levels = targetList.ToArray();
+    }
   }
 }
