Index: branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/DataTable.cs
===================================================================
--- branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/DataTable.cs	(revision 8228)
+++ branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/DataTable.cs	(revision 9363)
@@ -59,4 +59,9 @@
     public NamedItemCollection<DataRow> Rows {
       get { return rows; }
+      private set {
+        if (rows != null) throw new InvalidOperationException("Rows already set");
+        rows = value;
+        if (rows != null) RegisterRowsEvents();
+      }
     }
 
@@ -73,5 +78,5 @@
     private IEnumerable<DataRow> StorableRows {
       get { return rows; }
-      set { rows = new NamedItemCollection<DataRow>(value); }
+      set { Rows = new NamedItemCollection<DataRow>(value); }
     }
     #endregion
@@ -81,25 +86,21 @@
     protected DataTable(DataTable original, Cloner cloner)
       : base(original, cloner) {
-      this.VisualProperties = (DataTableVisualProperties)cloner.Clone(original.visualProperties);
-      this.rows = cloner.Clone(original.rows);
-      this.RegisterRowsEvents();
+      VisualProperties = (DataTableVisualProperties)cloner.Clone(original.visualProperties);
+      Rows = cloner.Clone(original.rows);
     }
     public DataTable()
       : base() {
       VisualProperties = new DataTableVisualProperties();
-      rows = new NamedItemCollection<DataRow>();
-      this.RegisterRowsEvents();
+      Rows = new NamedItemCollection<DataRow>();
     }
     public DataTable(string name)
       : base(name) {
       VisualProperties = new DataTableVisualProperties(name);
-      rows = new NamedItemCollection<DataRow>();
-      this.RegisterRowsEvents();
+      Rows = new NamedItemCollection<DataRow>();
     }
     public DataTable(string name, string description)
       : base(name, description) {
       VisualProperties = new DataTableVisualProperties(name);
-      rows = new NamedItemCollection<DataRow>();
-      this.RegisterRowsEvents();
+      Rows = new NamedItemCollection<DataRow>();
     }
 
@@ -217,5 +218,5 @@
     }
     IEnumerable<string> IStringConvertibleMatrix.RowNames {
-      get { return new List<string>(); }
+      get { return Enumerable.Empty<string>(); }
       set { throw new NotSupportedException(); }
     }
Index: branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/DataTableVisualProperties.cs
===================================================================
--- branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/DataTableVisualProperties.cs	(revision 8228)
+++ branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/DataTableVisualProperties.cs	(revision 9363)
@@ -20,8 +20,8 @@
 #endregion
 
+using HeuristicLab.Common;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 using System.ComponentModel;
 using System.Drawing;
-using HeuristicLab.Common;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
 namespace HeuristicLab.Analysis {
@@ -315,4 +315,44 @@
     }
 
+    private bool xAxisLogScale;
+    public bool XAxisLogScale {
+      get { return xAxisLogScale; }
+      set {
+        if (xAxisLogScale == value) return;
+        xAxisLogScale = value;
+        OnPropertyChanged("XAxisLogScale");
+      }
+    }
+
+    private bool secondXAxisLogScale;
+    public bool SecondXAxisLogScale {
+      get { return secondXAxisLogScale; }
+      set {
+        if (secondXAxisLogScale == value) return;
+        secondXAxisLogScale = value;
+        OnPropertyChanged("SecondXAxisLogScale");
+      }
+    }
+
+    private bool yAxisLogScale;
+    public bool YAxisLogScale {
+      get { return yAxisLogScale; }
+      set {
+        if (yAxisLogScale == value) return;
+        yAxisLogScale = value;
+        OnPropertyChanged("YAxisLogScale");
+      }
+    }
+
+    private bool secondYAxisLogScale;
+    public bool SecondYAxisLogScale {
+      get { return secondYAxisLogScale; }
+      set {
+        if (secondYAxisLogScale == value) return;
+        secondYAxisLogScale = value;
+        OnPropertyChanged("SecondYAxisLogScale");
+      }
+    }
+
     #region Persistence Properties
     [Storable(Name = "TitleFont")]
@@ -440,4 +480,24 @@
       get { return secondYAxisMaximumFixedValue; }
       set { secondYAxisMaximumFixedValue = value; }
+    }
+    [Storable(Name = "XAxisLogScale")]
+    private bool StorableXAxisLogScale {
+      get { return xAxisLogScale; }
+      set { xAxisLogScale = value; }
+    }
+    [Storable(Name = "SecondXAxisLogScale")]
+    private bool StorableSecondXAxisLogScale {
+      get { return secondXAxisLogScale; }
+      set { secondXAxisLogScale = value; }
+    }
+    [Storable(Name = "YAxisLogScale")]
+    private bool StorableYAxisLogScale {
+      get { return yAxisLogScale; }
+      set { yAxisLogScale = value; }
+    }
+    [Storable(Name = "SecondYAxisLogScale")]
+    private bool StorableSecondYAxisLogScale {
+      get { return secondYAxisLogScale; }
+      set { secondYAxisLogScale = value; }
     }
     #endregion
@@ -472,4 +532,8 @@
       this.secondYAxisMaximumAuto = original.secondYAxisMaximumAuto;
       this.secondYAxisMaximumFixedValue = original.secondYAxisMaximumFixedValue;
+      this.xAxisLogScale = original.xAxisLogScale;
+      this.secondXAxisLogScale = original.secondXAxisLogScale;
+      this.yAxisLogScale = original.yAxisLogScale;
+      this.secondYAxisLogScale = original.secondYAxisLogScale;
     }
     public DataTableVisualProperties() {
@@ -497,4 +561,8 @@
       this.secondYAxisMaximumAuto = true;
       this.secondYAxisMaximumFixedValue = double.NaN;
+      this.xAxisLogScale = false;
+      this.secondXAxisLogScale = false;
+      this.yAxisLogScale = false;
+      this.secondYAxisLogScale = false;
     }
     public DataTableVisualProperties(string title)
@@ -509,5 +577,5 @@
     public event PropertyChangedEventHandler PropertyChanged;
     protected virtual void OnPropertyChanged(string propertyName) {
-      PropertyChangedEventHandler handler = PropertyChanged;
+      var handler = PropertyChanged;
       if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
     }
@@ -547,6 +615,6 @@
         this.secondYAxisMaximumAuto = true;
         this.secondYAxisMaximumFixedValue = double.NaN;
+      }
       #endregion
-      }
     }
   }
Index: branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlot.cs
===================================================================
--- branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlot.cs	(revision 8228)
+++ branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlot.cs	(revision 9363)
@@ -22,4 +22,5 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Drawing;
 using System.Linq;
@@ -38,31 +39,43 @@
     }
 
-    [Storable]
-    private ObservableList<PointF> points;
-    public ObservableList<PointF> Points {
-      get { return points; }
-    }
-
-    [Storable]
-    private string xAxisName;
-    public string XAxisName {
-      get { return xAxisName; }
+    private ScatterPlotVisualProperties visualProperties;
+    public ScatterPlotVisualProperties VisualProperties {
+      get { return visualProperties; }
       set {
-        if (value == xAxisName) return;
-        xAxisName = value;
-        OnAxesNameChanged();
+        if (visualProperties != value) {
+          if (value == null) throw new ArgumentNullException("VisualProperties");
+          if (visualProperties != null) visualProperties.PropertyChanged -= new PropertyChangedEventHandler(VisualProperties_PropertyChanged);
+          visualProperties = value;
+          visualProperties.PropertyChanged += new PropertyChangedEventHandler(VisualProperties_PropertyChanged);
+          OnVisualPropertiesChanged();
+        }
       }
     }
 
-    [Storable]
-    private string yAxisName;
-    public string YAxisName {
-      get { return yAxisName; }
+    private NamedItemCollection<ScatterPlotDataRow> rows;
+    public NamedItemCollection<ScatterPlotDataRow> Rows {
+      get { return rows; }
+      private set {
+        if (rows != null) throw new InvalidOperationException("Rows already set");
+        rows = value;
+        if (rows != null) RegisterRowsEvents();
+      }
+    }
+
+    #region Persistence Properties
+    [Storable(Name = "VisualProperties")]
+    private ScatterPlotVisualProperties StorableVisualProperties {
+      get { return visualProperties; }
       set {
-        if (value == yAxisName) return;
-        yAxisName = value;
-        OnAxesNameChanged();
+        visualProperties = value;
+        visualProperties.PropertyChanged += new PropertyChangedEventHandler(VisualProperties_PropertyChanged);
       }
     }
+    [Storable(Name = "Rows")]
+    private IEnumerable<ScatterPlotDataRow> StorableRows {
+      get { return rows; }
+      set { Rows = new NamedItemCollection<ScatterPlotDataRow>(value); }
+    }
+    #endregion
 
     [StorableConstructor]
@@ -70,44 +83,49 @@
     protected ScatterPlot(ScatterPlot original, Cloner cloner)
       : base(original, cloner) {
-      points = new ObservableList<PointF>(original.points);
-      xAxisName = original.xAxisName;
-      yAxisName = original.yAxisName;
+      VisualProperties = cloner.Clone(original.visualProperties);
+      Rows = cloner.Clone(original.rows);
     }
     public ScatterPlot()
       : base() {
-      this.points = new ObservableList<PointF>();
-    }
-    public ScatterPlot(string name)
-      : base(name) {
-      this.points = new ObservableList<PointF>();
+      VisualProperties = new ScatterPlotVisualProperties();
+      Rows = new NamedItemCollection<ScatterPlotDataRow>();
     }
     public ScatterPlot(string name, string description)
       : base(name, description) {
-      this.points = new ObservableList<PointF>();
-    }
-    public ScatterPlot(string name, string description, string xAxisName, string yAxisName)
+      VisualProperties = new ScatterPlotVisualProperties(name);
+      Rows = new NamedItemCollection<ScatterPlotDataRow>();
+    }
+    public ScatterPlot(string name, string description, ScatterPlotVisualProperties visualProperties)
       : base(name, description) {
-      this.points = new ObservableList<PointF>();
-      this.xAxisName = xAxisName;
-      this.yAxisName = yAxisName;
-    }
-    public ScatterPlot(IEnumerable<PointF> points)
-      : base() {
-      this.points = new ObservableList<PointF>(points);
-    }
-    public ScatterPlot(IEnumerable<PointF> points, string name)
-      : base(name) {
-      this.points = new ObservableList<PointF>(points);
-    }
-    public ScatterPlot(IEnumerable<PointF> points, string name, string description)
-      : base(name, description) {
-      this.points = new ObservableList<PointF>(points);
-    }
-    public ScatterPlot(IEnumerable<PointF> points, string name, string description, string xAxisName, string yAxisName)
-      : base(name, description) {
-      this.points = new ObservableList<PointF>(points);
-      this.xAxisName = xAxisName;
-      this.yAxisName = yAxisName;
-    }
+      VisualProperties = visualProperties;
+      Rows = new NamedItemCollection<ScatterPlotDataRow>();
+    }
+
+    // BackwardsCompatibility3.3
+    #region Backwards compatible code, remove with 3.4
+    private ObservableList<PointF> points;
+    [Storable(Name = "points", AllowOneWay = true)]
+    private ObservableList<PointF> StorablePoints {
+      set { points = value; }
+    }
+    private string xAxisName;
+    [Storable(Name = "xAxisName", AllowOneWay = true)]
+    private string StorableXAxisName {
+      set { xAxisName = value; }
+    }
+    private string yAxisName;
+    [Storable(Name = "yAxisName", AllowOneWay = true)]
+    private string StorableYAxisName {
+      set { yAxisName = value; }
+    }
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if (VisualProperties == null) VisualProperties = new ScatterPlotVisualProperties(name);
+      if (string.IsNullOrEmpty(VisualProperties.XAxisTitle) && !string.IsNullOrEmpty(xAxisName)) VisualProperties.XAxisTitle = xAxisName;
+      if (string.IsNullOrEmpty(VisualProperties.YAxisTitle) && !string.IsNullOrEmpty(yAxisName)) VisualProperties.YAxisTitle = yAxisName;
+      if (rows == null) Rows = new NamedItemCollection<ScatterPlotDataRow>();
+      if ((Rows.Count == 0) && (points != null)) Rows.Add(new ScatterPlotDataRow(name, null, points.Select(p => new Point2D<double>(p.X, p.Y))));
+    }
+    #endregion
 
     public override IDeepCloneable Clone(Cloner cloner) {
@@ -115,23 +133,100 @@
     }
 
-    public event EventHandler AxisNameChanged;
-    protected virtual void OnAxesNameChanged() {
-      EventHandler handler = AxisNameChanged;
-      if (handler != null)
-        handler(this, EventArgs.Empty);
+    public event EventHandler VisualPropertiesChanged;
+    protected virtual void OnVisualPropertiesChanged() {
+      EventHandler handler = VisualPropertiesChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    private void VisualProperties_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      OnVisualPropertiesChanged();
+    }
+
+    protected virtual void RegisterRowsEvents() {
+      rows.ItemsAdded += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(rows_ItemsAdded);
+      rows.ItemsRemoved += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(rows_ItemsRemoved);
+      rows.ItemsReplaced += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(rows_ItemsReplaced);
+      rows.CollectionReset += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(rows_CollectionReset);
+    }
+    private void rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
+      foreach (ScatterPlotDataRow row in e.Items)
+        this.RegisterRowEvents(row);
+
+      this.OnColumnsChanged();
+      this.OnColumnNamesChanged();
+      this.OnReset();
+    }
+    private void rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
+      foreach (ScatterPlotDataRow row in e.Items)
+        this.DeregisterRowEvents(row);
+
+      this.OnColumnsChanged();
+      this.OnColumnNamesChanged();
+      this.OnReset();
+    }
+    private void rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
+      foreach (ScatterPlotDataRow row in e.OldItems)
+        this.DeregisterRowEvents(row);
+      foreach (ScatterPlotDataRow row in e.Items)
+        this.RegisterRowEvents(row);
+
+      this.OnColumnsChanged();
+      this.OnColumnNamesChanged();
+      this.OnReset();
+    }
+    private void rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
+      foreach (ScatterPlotDataRow row in e.OldItems)
+        this.DeregisterRowEvents(row);
+      foreach (ScatterPlotDataRow row in e.Items)
+        this.RegisterRowEvents(row);
+
+      if (e.OldItems.Count() != e.Items.Count())
+        this.OnColumnsChanged();
+      this.OnColumnNamesChanged();
+      this.OnReset();
+    }
+
+    protected virtual void RegisterRowEvents(ScatterPlotDataRow row) {
+      row.Points.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded);
+      row.Points.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsMoved);
+      row.Points.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved);
+      row.Points.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced);
+      row.Points.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset);
+    }
+    protected virtual void DeregisterRowEvents(ScatterPlotDataRow row) {
+      row.Points.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded);
+      row.Points.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsMoved);
+      row.Points.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved);
+      row.Points.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced);
+      row.Points.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset);
+    }
+
+    private void Points_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
+      this.OnReset();
+    }
+    private void Points_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
+      this.OnReset();
+    }
+    private void Points_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
+      this.OnReset();
+    }
+    private void Points_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
+      this.OnReset();
+    }
+    private void Points_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
+      this.OnReset();
     }
 
     #region IStringConvertibleMatrix Members
-
     int IStringConvertibleMatrix.Rows {
-      get { return points.Count; }
+      get { return rows.Count == 0 ? 0 : rows.Max(r => r.Points.Count); }
       set { throw new NotSupportedException(); }
     }
     int IStringConvertibleMatrix.Columns {
-      get { return 2; }
+      get { return rows.Count; }
       set { throw new NotSupportedException(); }
     }
     IEnumerable<string> IStringConvertibleMatrix.ColumnNames {
-      get { return new string[] { xAxisName, yAxisName }; }
+      get { return rows.Select(r => r.Name); }
       set { throw new NotSupportedException(); }
     }
@@ -142,5 +237,5 @@
 
     bool IStringConvertibleMatrix.SortableView {
-      get { return false; }
+      get { return true; }
       set { throw new NotSupportedException(); }
     }
@@ -150,6 +245,8 @@
 
     string IStringConvertibleMatrix.GetValue(int rowIndex, int columnIndex) {
-      if (rowIndex < points.Count && columnIndex < 2) {
-        return columnIndex == 0 ? points[rowIndex].X.ToString() : points[rowIndex].Y.ToString();
+      if (columnIndex < rows.Count) {
+        string columnName = ((IStringConvertibleMatrix)this).ColumnNames.ElementAt(columnIndex);
+        if (rows.ContainsKey(columnName) && rowIndex < rows[columnName].Points.Count)
+          return string.Format("{0};{1}", rows[columnName].Points[rowIndex].X, rows[columnName].Points[rowIndex].Y);
       }
       return string.Empty;
Index: branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotDataRow.cs
===================================================================
--- branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotDataRow.cs	(revision 9363)
+++ branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotDataRow.cs	(revision 9363)
@@ -0,0 +1,108 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Analysis {
+  /// <summary>
+  /// A row of data values for a scatter plot.
+  /// </summary>
+  [Item("ScatterPlotDataRow", "A row of data values for a scatter plot.")]
+  [StorableClass]
+  public class ScatterPlotDataRow : NamedItem {
+    private ScatterPlotDataRowVisualProperties visualProperties;
+    public ScatterPlotDataRowVisualProperties VisualProperties {
+      get { return visualProperties; }
+      set {
+        if (visualProperties != value) {
+          if (value == null) throw new ArgumentNullException("VisualProperties");
+          if (visualProperties != null) visualProperties.PropertyChanged -= new PropertyChangedEventHandler(VisualProperties_PropertyChanged);
+          visualProperties = value;
+          visualProperties.PropertyChanged += new PropertyChangedEventHandler(VisualProperties_PropertyChanged);
+          OnVisualPropertiesChanged();
+        }
+      }
+    }
+    private ObservableList<Point2D<double>> points;
+    public ObservableList<Point2D<double>> Points {
+      get { return points; }
+    }
+
+    #region Persistence Properties
+    [Storable(Name = "VisualProperties")]
+    private ScatterPlotDataRowVisualProperties StorableVisualProperties {
+      get { return visualProperties; }
+      set {
+        visualProperties = value;
+        visualProperties.PropertyChanged += new PropertyChangedEventHandler(VisualProperties_PropertyChanged);
+      }
+    }
+    [Storable(Name = "Points")]
+    private IEnumerable<Point2D<double>> StorablePoints {
+      get { return points; }
+      set { points = new ObservableList<Point2D<double>>(value); }
+    }
+    #endregion
+
+    [StorableConstructor]
+    protected ScatterPlotDataRow(bool deserializing) : base(deserializing) { }
+    protected ScatterPlotDataRow(ScatterPlotDataRow original, Cloner cloner)
+      : base(original, cloner) {
+      VisualProperties = cloner.Clone(original.visualProperties);
+      points = new ObservableList<Point2D<double>>(original.points);
+    }
+    public ScatterPlotDataRow()
+      : base() {
+      VisualProperties = new ScatterPlotDataRowVisualProperties();
+      points = new ObservableList<Point2D<double>>();
+    }
+    public ScatterPlotDataRow(string name, string description, IEnumerable<Point2D<double>> points)
+      : base(name, description) {
+      VisualProperties = new ScatterPlotDataRowVisualProperties(name);
+      this.points = new ObservableList<Point2D<double>>(points);
+    }
+    public ScatterPlotDataRow(string name, string description, IEnumerable<Point2D<double>> points, ScatterPlotDataRowVisualProperties visualProperties)
+      : base(name, description) {
+      VisualProperties = visualProperties;
+      this.points = new ObservableList<Point2D<double>>(points);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ScatterPlotDataRow(this, cloner);
+    }
+
+    public event EventHandler VisualPropertiesChanged;
+    protected virtual void OnVisualPropertiesChanged() {
+      EventHandler handler = VisualPropertiesChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    private void VisualProperties_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      OnVisualPropertiesChanged();
+    }
+  }
+}
Index: branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotDataRowVisualProperties.cs
===================================================================
--- branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotDataRowVisualProperties.cs	(revision 9363)
+++ branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotDataRowVisualProperties.cs	(revision 9363)
@@ -0,0 +1,164 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using HeuristicLab.Common;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Analysis {
+  /// <summary>
+  /// Visual properties of a ScatterPlotDataRow.
+  /// </summary>
+  [StorableClass]
+  public class ScatterPlotDataRowVisualProperties : DeepCloneable, INotifyPropertyChanged {
+    #region PointStyle
+    public enum ScatterPlotDataRowPointStyle {
+      Circle,
+      Cross,
+      Diamond,
+      Square,
+      Star4,
+      Star5,
+      Star6,
+      Star10,
+      Triangle
+    }
+    #endregion
+
+    private Color color;
+    public Color Color {
+      get { return color; }
+      set {
+        if (color != value) {
+          color = value;
+          OnPropertyChanged("Color");
+        }
+      }
+    }
+    private ScatterPlotDataRowPointStyle pointStyle;
+    public ScatterPlotDataRowPointStyle PointStyle {
+      get { return pointStyle; }
+      set {
+        if (pointStyle != value) {
+          pointStyle = value;
+          OnPropertyChanged("PointStyle");
+        }
+      }
+    }
+    private int pointSize;
+    public int PointSize {
+      get { return pointSize; }
+      set {
+        if (pointSize != value) {
+          pointSize = value;
+          OnPropertyChanged("PointSize");
+        }
+      }
+    }
+    private bool isVisibleInLegend;
+    public bool IsVisibleInLegend {
+      get { return isVisibleInLegend; }
+      set {
+        if (isVisibleInLegend != value) {
+          isVisibleInLegend = value;
+          OnPropertyChanged("IsVisibleInLegend");
+        }
+      }
+    }
+    private string displayName;
+    public string DisplayName {
+      get { return displayName == null ? String.Empty : displayName; }
+      set {
+        if (displayName != value) {
+          if (value == null && displayName != String.Empty) {
+            displayName = String.Empty;
+            OnPropertyChanged("DisplayName");
+          } else if (value != null) {
+            displayName = value;
+            OnPropertyChanged("DisplayName");
+          }
+        }
+      }
+    }
+
+    #region Persistence Properties
+    [Storable(Name = "Color")]
+    private Color StorableColor {
+      get { return color; }
+      set { color = value; }
+    }
+    [Storable(Name = "PointStyle")]
+    private ScatterPlotDataRowPointStyle StorablePointStyle {
+      get { return pointStyle; }
+      set { pointStyle = value; }
+    }
+    [Storable(Name = "PointSize")]
+    private int StorablePointSize {
+      get { return pointSize; }
+      set { pointSize = value; }
+    }
+    [Storable(Name = "IsVisibleInLegend")]
+    private bool StorableIsVisibleInLegend {
+      get { return isVisibleInLegend; }
+      set { isVisibleInLegend = value; }
+    }
+    [Storable(Name = "DisplayName")]
+    private string StorableDisplayName {
+      get { return displayName; }
+      set { displayName = value; }
+    }
+    #endregion
+
+    [StorableConstructor]
+    protected ScatterPlotDataRowVisualProperties(bool deserializing) : base() { }
+    protected ScatterPlotDataRowVisualProperties(ScatterPlotDataRowVisualProperties original, Cloner cloner)
+      : base(original, cloner) {
+      this.color = original.color;
+      this.pointStyle = original.pointStyle;
+      this.pointSize = original.pointSize;
+      this.displayName = original.displayName;
+      this.isVisibleInLegend = original.isVisibleInLegend;
+    }
+    public ScatterPlotDataRowVisualProperties() {
+      color = Color.Empty;
+      pointStyle = ScatterPlotDataRowPointStyle.Circle;
+      pointSize = 3;
+      displayName = String.Empty;
+      isVisibleInLegend = true;
+    }
+    public ScatterPlotDataRowVisualProperties(string displayName)
+      : this() {
+      this.displayName = displayName;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ScatterPlotDataRowVisualProperties(this, cloner);
+    }
+
+    public event PropertyChangedEventHandler PropertyChanged;
+    protected virtual void OnPropertyChanged(string propertyName) {
+      PropertyChangedEventHandler handler = PropertyChanged;
+      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
+    }
+  }
+}
Index: branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotHistory.cs
===================================================================
--- branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotHistory.cs	(revision 9363)
+++ branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotHistory.cs	(revision 9363)
@@ -0,0 +1,49 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using System.Drawing;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Analysis {
+  /// <summary>
+  /// Represents history values of scatter plots.
+  /// </summary>
+  [Item("ScatterPlotHistory", "Represents history values of scatter plots.")]
+  [StorableClass]
+  public class ScatterPlotHistory : ItemCollection<ScatterPlot> {
+    public static new Image StaticItemImage {
+      get { return HeuristicLab.Common.Resources.VSImageLibrary.Cab; }
+    }
+
+    [StorableConstructor]
+    protected ScatterPlotHistory(bool deserializing) : base(deserializing) { }
+    protected ScatterPlotHistory(ScatterPlotHistory original, Cloner cloner) : base(original, cloner) { }
+    public ScatterPlotHistory() : base() { }
+    public ScatterPlotHistory(IEnumerable<ScatterPlot> scatterPlots) : base(new ItemCollection<ScatterPlot>(scatterPlots)) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ScatterPlotHistory(this, cloner);
+    }
+  }
+}
Index: branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotVisualProperties.cs
===================================================================
--- branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotVisualProperties.cs	(revision 9363)
+++ branches/OaaS/HeuristicLab.Analysis/3.3/DataVisualization/ScatterPlotVisualProperties.cs	(revision 9363)
@@ -0,0 +1,365 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.ComponentModel;
+using System.Drawing;
+using HeuristicLab.Common;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Analysis {
+  /// <summary>
+  /// Visual properties of a ScatterPlot.
+  /// </summary>
+  [StorableClass]
+  public class ScatterPlotVisualProperties : DeepCloneable, INotifyPropertyChanged {
+    private Font titleFont;
+    public Font TitleFont {
+      get { return titleFont; }
+      set {
+        if (titleFont == null || value == null
+          || titleFont.Name != value.Name || titleFont.Size != value.Size || titleFont.Style != value.Style) {
+          titleFont = value;
+          OnPropertyChanged("TitleFont");
+        }
+      }
+    }
+    private Color titleColor;
+    public Color TitleColor {
+      get { return titleColor; }
+      set {
+        if (titleColor != value) {
+          titleColor = value;
+          OnPropertyChanged("TitleFontColor");
+        }
+      }
+    }
+    private string title;
+    public string Title {
+      get { return title; }
+      set {
+        if (title != value) {
+          title = value;
+          OnPropertyChanged("Title");
+        }
+      }
+    }
+    private Font axisTitleFont;
+    public Font AxisTitleFont {
+      get { return axisTitleFont; }
+      set {
+        if (axisTitleFont == null || axisTitleFont == null
+          || axisTitleFont.Name != value.Name || axisTitleFont.Size != value.Size || axisTitleFont.Style != value.Style) {
+          axisTitleFont = value;
+          OnPropertyChanged("AxisTitleFont");
+        }
+      }
+    }
+    private Color axisTitleColor;
+    public Color AxisTitleColor {
+      get { return axisTitleColor; }
+      set {
+        if (axisTitleColor != value) {
+          axisTitleColor = value;
+          OnPropertyChanged("AxisTitleColor");
+        }
+      }
+    }
+    private string xAxisTitle;
+    public string XAxisTitle {
+      get { return xAxisTitle; }
+      set {
+        if (value == null) value = string.Empty;
+        if (xAxisTitle != value) {
+          xAxisTitle = value;
+          OnPropertyChanged("XAxisTitle");
+        }
+      }
+    }
+
+    private string yAxisTitle;
+    public string YAxisTitle {
+      get { return yAxisTitle; }
+      set {
+        if (value == null) value = string.Empty;
+        if (yAxisTitle != value) {
+          yAxisTitle = value;
+          OnPropertyChanged("YAxisTitle");
+        }
+      }
+    }
+
+    private bool xAxisGrid;
+    public bool XAxisGrid {
+      get { return xAxisGrid; }
+      set {
+        if (xAxisGrid != value) {
+          xAxisGrid = value;
+          OnPropertyChanged("XAxisGrid");
+        }
+      }
+    }
+
+    private bool yAxisGrid;
+    public bool YAxisGrid {
+      get { return yAxisGrid; }
+      set {
+        if (yAxisGrid != value) {
+          yAxisGrid = value;
+          OnPropertyChanged("YAxisGrid");
+        }
+      }
+    }
+
+    private bool xAxisMinimumAuto;
+    public bool XAxisMinimumAuto {
+      get { return xAxisMinimumAuto; }
+      set {
+        if (xAxisMinimumAuto != value) {
+          xAxisMinimumAuto = value;
+          if (value) xAxisMinimumFixedValue = double.NaN;
+          OnPropertyChanged("XAxisMinimumAuto");
+        }
+      }
+    }
+
+    private bool xAxisMaximumAuto;
+    public bool XAxisMaximumAuto {
+      get { return xAxisMaximumAuto; }
+      set {
+        if (xAxisMaximumAuto != value) {
+          xAxisMaximumAuto = value;
+          if (value) xAxisMaximumFixedValue = double.NaN;
+          OnPropertyChanged("XAxisMaximumAuto");
+        }
+      }
+    }
+
+    private double xAxisMinimumFixedValue;
+    public double XAxisMinimumFixedValue {
+      get { return xAxisMinimumFixedValue; }
+      set {
+        if (xAxisMinimumFixedValue != value) {
+          xAxisMinimumFixedValue = value;
+          OnPropertyChanged("XAxisMinimumFixedValue");
+        }
+      }
+    }
+
+    private double xAxisMaximumFixedValue;
+    public double XAxisMaximumFixedValue {
+      get { return xAxisMaximumFixedValue; }
+      set {
+        if (xAxisMaximumFixedValue != value) {
+          xAxisMaximumFixedValue = value;
+          OnPropertyChanged("XAxisMaximumFixedValue");
+        }
+      }
+    }
+
+    private bool yAxisMinimumAuto;
+    public bool YAxisMinimumAuto {
+      get { return yAxisMinimumAuto; }
+      set {
+        if (yAxisMinimumAuto != value) {
+          yAxisMinimumAuto = value;
+          if (value) yAxisMinimumFixedValue = double.NaN;
+          OnPropertyChanged("YAxisMinimumAuto");
+        }
+      }
+    }
+
+    private bool yAxisMaximumAuto;
+    public bool YAxisMaximumAuto {
+      get { return yAxisMaximumAuto; }
+      set {
+        if (yAxisMaximumAuto != value) {
+          yAxisMaximumAuto = value;
+          if (value) yAxisMaximumFixedValue = double.NaN;
+          OnPropertyChanged("YAxisMaximumAuto");
+        }
+      }
+    }
+
+    private double yAxisMinimumFixedValue;
+    public double YAxisMinimumFixedValue {
+      get { return yAxisMinimumFixedValue; }
+      set {
+        if (yAxisMinimumFixedValue != value) {
+          yAxisMinimumFixedValue = value;
+          OnPropertyChanged("YAxisMinimumFixedValue");
+        }
+      }
+    }
+
+    private double yAxisMaximumFixedValue;
+    public double YAxisMaximumFixedValue {
+      get { return yAxisMaximumFixedValue; }
+      set {
+        if (yAxisMaximumFixedValue != value) {
+          yAxisMaximumFixedValue = value;
+          OnPropertyChanged("YAxisMaximumFixedValue");
+        }
+      }
+    }
+
+    #region Persistence Properties
+    [Storable(Name = "TitleFont")]
+    private Font StorableTitleFont {
+      get { return titleFont; }
+      set { titleFont = value; }
+    }
+    [Storable(Name = "TitleColor")]
+    private Color StorableTitleColor {
+      get { return titleColor; }
+      set { titleColor = value; }
+    }
+    [Storable(Name = "Title")]
+    private string StorableTitle {
+      get { return title; }
+      set { title = value; }
+    }
+    [Storable(Name = "AxisTitleFont")]
+    private Font StorableAxisTitleFont {
+      get { return axisTitleFont; }
+      set { axisTitleFont = value; }
+    }
+    [Storable(Name = "AxisTitleColor")]
+    private Color StorableAxisTitleColor {
+      get { return axisTitleColor; }
+      set { axisTitleColor = value; }
+    }
+    [Storable(Name = "XAxisTitle")]
+    private string StorableXAxisTitle {
+      get { return xAxisTitle; }
+      set { xAxisTitle = value; }
+    }
+    [Storable(Name = "YAxisTitle")]
+    private string StorableYAxisTitle {
+      get { return yAxisTitle; }
+      set { yAxisTitle = value; }
+    }
+    [Storable(Name = "XAxisGrid")]
+    private bool StorableXAxisGrid {
+      get { return xAxisGrid; }
+      set { xAxisGrid = value; }
+    }
+    [Storable(Name = "YAxisGrid")]
+    private bool StorableYAxisGrid {
+      get { return yAxisGrid; }
+      set { yAxisGrid = value; }
+    }
+    [Storable(Name = "XAxisMinimumAuto")]
+    private bool StorableXAxisMinimumAuto {
+      get { return xAxisMinimumAuto; }
+      set { xAxisMinimumAuto = value; }
+    }
+    [Storable(Name = "XAxisMaximumAuto")]
+    private bool StorableXAxisMaximumAuto {
+      get { return xAxisMaximumAuto; }
+      set { xAxisMaximumAuto = value; }
+    }
+    [Storable(Name = "XAxisMinimumFixedValue")]
+    private double StorableXAxisMinimumFixedValue {
+      get { return xAxisMinimumFixedValue; }
+      set { xAxisMinimumFixedValue = value; }
+    }
+    [Storable(Name = "XAxisMaximumFixedValue")]
+    private double StorableXAxisMaximumFixedValue {
+      get { return xAxisMaximumFixedValue; }
+      set { xAxisMaximumFixedValue = value; }
+    }
+    [Storable(Name = "YAxisMinimumAuto")]
+    private bool StorableYAxisMinimumAuto {
+      get { return yAxisMinimumAuto; }
+      set { yAxisMinimumAuto = value; }
+    }
+    [Storable(Name = "YAxisMaximumAuto")]
+    private bool StorableYAxisMaximumAuto {
+      get { return yAxisMaximumAuto; }
+      set { yAxisMaximumAuto = value; }
+    }
+    [Storable(Name = "YAxisMinimumFixedValue")]
+    private double StorableYAxisMinimumFixedValue {
+      get { return yAxisMinimumFixedValue; }
+      set { yAxisMinimumFixedValue = value; }
+    }
+    [Storable(Name = "YAxisMaximumFixedValue")]
+    private double StorableYAxisMaximumFixedValue {
+      get { return yAxisMaximumFixedValue; }
+      set { yAxisMaximumFixedValue = value; }
+    }
+    #endregion
+
+    [StorableConstructor]
+    protected ScatterPlotVisualProperties(bool deserializing) : base() { }
+    protected ScatterPlotVisualProperties(ScatterPlotVisualProperties original, Cloner cloner)
+      : base(original, cloner) {
+      if (original.titleFont != null)
+        this.titleFont = (Font)original.titleFont.Clone();
+      if (original.axisTitleFont != null)
+        this.axisTitleFont = (Font)original.axisTitleFont.Clone();
+      this.title = original.title;
+      this.xAxisTitle = original.xAxisTitle;
+      this.yAxisTitle = original.yAxisTitle;
+      this.xAxisGrid = original.xAxisGrid;
+      this.yAxisGrid = original.yAxisGrid;
+      this.xAxisMinimumAuto = original.xAxisMinimumAuto;
+      this.xAxisMinimumFixedValue = original.xAxisMinimumFixedValue;
+      this.xAxisMaximumAuto = original.xAxisMaximumAuto;
+      this.xAxisMaximumFixedValue = original.xAxisMaximumFixedValue;
+      this.yAxisMinimumAuto = original.yAxisMinimumAuto;
+      this.yAxisMinimumFixedValue = original.yAxisMinimumFixedValue;
+      this.yAxisMaximumAuto = original.yAxisMaximumAuto;
+      this.yAxisMaximumFixedValue = original.yAxisMaximumFixedValue;
+    }
+    public ScatterPlotVisualProperties() {
+      this.titleColor = Color.Black;
+      this.axisTitleColor = Color.Black;
+      this.title = string.Empty;
+      this.xAxisTitle = string.Empty;
+      this.yAxisTitle = string.Empty;
+      this.xAxisGrid = true;
+      this.yAxisGrid = true;
+      this.xAxisMinimumAuto = true;
+      this.xAxisMinimumFixedValue = double.NaN;
+      this.xAxisMaximumAuto = true;
+      this.xAxisMaximumFixedValue = double.NaN;
+      this.yAxisMinimumAuto = true;
+      this.yAxisMinimumFixedValue = double.NaN;
+      this.yAxisMaximumAuto = true;
+      this.yAxisMaximumFixedValue = double.NaN;
+    }
+    public ScatterPlotVisualProperties(string title)
+      : this() {
+      this.title = title;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ScatterPlotVisualProperties(this, cloner);
+    }
+
+    public event PropertyChangedEventHandler PropertyChanged;
+    protected virtual void OnPropertyChanged(string propertyName) {
+      PropertyChangedEventHandler handler = PropertyChanged;
+      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
+    }
+  }
+}
