Index: branches/HeuristicLab.RegressionSolutionGradientView/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs
===================================================================
--- branches/HeuristicLab.RegressionSolutionGradientView/HeuristicLab.Problems.DataAnalysis.Views/3.4/RegressionSolutionTargetResponseGradientView.cs	(revision 13846)
+++ branches/HeuristicLab.RegressionSolutionGradientView/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs	(revision 13850)
@@ -22,4 +22,5 @@
 using System;
 using System.Collections.Generic;
+using System.Drawing;
 using System.Linq;
 using System.Threading.Tasks;
@@ -33,16 +34,40 @@
   [Content(typeof(IRegressionSolution))]
   public partial class RegressionSolutionTargetResponseGradientView : DataAnalysisSolutionEvaluationView {
-    private readonly Dictionary<string, GradientChart> charts;
+    private readonly Dictionary<string, GradientChart> gradientCharts;
+    private readonly Dictionary<string, DensityChart> densityCharts;
+    private readonly Dictionary<string, Panel> groupingPanels;
+
     private const int Points = 200;
-
-    private IEnumerable<GradientChart> VisibleCharts {
-      get { return gradientChartTableLayout.Controls.OfType<GradientChart>(); }
+    private const int MaxColumns = 4;
+
+    private IEnumerable<string> VisibleVariables {
+      get {
+        foreach (ListViewItem item in variableListView.CheckedItems)
+          yield return item.Text;
+      }
+    }
+    private IEnumerable<GradientChart> VisibleGradientCharts {
+      get { return VisibleVariables.Select(v => gradientCharts[v]); }
+    }
+    private IEnumerable<DensityChart> VisibleDensityCharts {
+      get { return VisibleVariables.Select(v => densityCharts[v]); }
+    }
+    private IEnumerable<Panel> VisibleChartsPanels {
+      get { return VisibleVariables.Select(v => groupingPanels[v]); }
     }
 
     public RegressionSolutionTargetResponseGradientView() {
       InitializeComponent();
-      charts = new Dictionary<string, GradientChart>();
+      gradientCharts = new Dictionary<string, GradientChart>();
+      densityCharts = new Dictionary<string, DensityChart>();
+      groupingPanels = new Dictionary<string, Panel>();
 
       limitView.Content = new DoubleLimit(0, 1);
+      densityComboBox.SelectedIndex = 0; // select None
+
+      // Avoid additional horizontal scrollbar
+      var vertScrollWidth = SystemInformation.VerticalScrollBarWidth;
+      scrollPanel.Padding = new Padding(0, 0, vertScrollWidth, 0);
+      scrollPanel.AutoScroll = true;
     }
 
@@ -91,28 +116,34 @@
       var variableValues = allowedInputVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
       var sharedFixedVariables = new ModifiableDataset(allowedInputVariables, variableValues);
-      // create charts
-      charts.Clear();
+
+      // create controls
+      gradientCharts.Clear();
+      densityCharts.Clear();
+      groupingPanels.Clear();
       foreach (var variableName in allowedInputVariables) {
-        var gradientChart = new GradientChart {
+        var gradientChart = CreateGradientChart(variableName, sharedFixedVariables);
+        gradientCharts.Add(variableName, gradientChart);
+
+        var densityChart = new DensityChart() {
+          //Dock = DockStyle.Top,
+          //Location = new Point(0, 0),
+          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
+          Margin = Padding.Empty,
+          Height = 12,
+          Visible = false
+        };
+        densityCharts.Add(variableName, densityChart);
+
+        var panel = new Panel() {
           Dock = DockStyle.Fill,
           Margin = Padding.Empty,
-          ShowLegend = false,
-          ShowCursor = true,
-          ShowXAxisLabel = true,
-          ShowYAxisLabel = true,
-          YAxisTicks = 5,
+          BackColor = Color.White
         };
-        gradientChart.VariableValueChanged += async (o, e) => {
-          var recalculations = VisibleCharts.Except(new[] { (GradientChart)o }).Select(async chart => {
-            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
-          }).ToList();
-          await Task.WhenAll(recalculations);
-
-          if (recalculations.All(t => t.IsCompleted))
-            SetupYAxis();
-        };
-        gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, Points);
-        charts[variableName] = gradientChart;
-      }
+
+        panel.Controls.Add(densityChart);
+        panel.Controls.Add(gradientChart);
+        groupingPanels.Add(variableName, panel);
+      }
+
       // update variable list
       variableListView.Items.Clear();
@@ -123,4 +154,27 @@
         variableListView.Items[variable].Checked = true;
     }
+    private GradientChart CreateGradientChart(string variableName, ModifiableDataset sharedFixedVariables) {
+      var gradientChart = new GradientChart {
+        Dock = DockStyle.Fill,
+        Margin = Padding.Empty,
+        ShowLegend = false,
+        ShowCursor = true,
+        ShowXAxisLabel = true,
+        ShowYAxisLabel = true,
+        YAxisTicks = 5,
+      };
+      gradientChart.VariableValueChanged += async (o, e) => {
+        var recalculations = VisibleGradientCharts.Except(new[] { (GradientChart)o }).Select(async chart => {
+          await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
+        }).ToList();
+        await Task.WhenAll(recalculations);
+
+        if (recalculations.All(t => t.IsCompleted))
+          SetupYAxis();
+      };
+      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, Points);
+
+      return gradientChart;
+    }
 
     private void SetupYAxis() {
@@ -128,5 +182,5 @@
       if (automaticYAxisCheckBox.Checked) {
         double min = double.MaxValue, max = double.MinValue;
-        foreach (var chart in VisibleCharts) {
+        foreach (var chart in VisibleGradientCharts) {
           if (chart.YMin < min) min = chart.YMin;
           if (chart.YMax > max) max = chart.YMax;
@@ -140,55 +194,66 @@
       }
 
-      foreach (var chart in VisibleCharts) {
+      foreach (var chart in VisibleGradientCharts) {
         chart.FixedYAxisMin = axisMin;
         chart.FixedYAxisMax = axisMax;
-        chart.Update();
-      }
-    }
-
-    // sort chart controls so that they always appear in the same order as in the list view
-    // the gradient chart layout should be suspended before calling this method
-    private void SortControls() {
+        //chart.Update();
+      }
+    }
+
+    // reorder chart controls so that they always appear in the same order as in the list view
+    // the table layout containing the controls should be suspended before calling this method
+    private void ReOrderControls() {
       var tl = gradientChartTableLayout;
-      var indices = variableListView.CheckedItems.Cast<ListViewItem>().ToDictionary(checkedItem => checkedItem.Text, checkedItem => checkedItem.Index);
-      var count = tl.Controls.Count;
-      var charts = new GradientChart[count];
-      for (int i = 0; i < count; ++i) charts[i] = (GradientChart)tl.Controls[i];
-      Array.Sort(charts, (a, b) => indices[a.FreeVariable].CompareTo(indices[b.FreeVariable]));
       tl.Controls.Clear();
-      tl.Controls.AddRange(charts);
+      int row = 0, column = 0;
+      foreach (var chartsPanel in VisibleChartsPanels) {
+        tl.Controls.Add(chartsPanel, column, row);
+        column++;
+        if (column == MaxColumns) {
+          row++;
+          column = 0;
+        }
+      }
     }
 
     private async void variableListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
-      if (charts == null) return;
       var item = e.Item;
       var variable = item.Text;
-      var chart = charts[variable];
+      var gradientChart = gradientCharts[variable];
+      var densityChart = densityCharts[variable];
+      var chartsPanel = groupingPanels[variable];
       var tl = gradientChartTableLayout;
+
       tl.SuspendLayout();
-      var columnWidth = tl.GetColumnWidths()[0]; // all columns have the same width
-      var rowHeight = 0.8f * columnWidth;
-      tl.RowStyles.Clear();
+
       if (item.Checked) {
-        tl.Controls.Add(chart);
-        await chart.RecalculateAsync();
+        tl.Controls.Add(chartsPanel);
+        await gradientChart.RecalculateAsync();
+        UpdateDensityChart(densityChart, variable);
       } else {
-        tl.Controls.Remove(chart);
-      }
-      SetupYAxis();
-
-      var count = tl.Controls.Count;
-      SortControls();
-      tl.RowCount = count / tl.ColumnCount + (count % tl.ColumnCount == 0 ? 0 : 1);
-      for (int i = 0; i < count; ++i) {
-        var control = tl.Controls[i];
-        int rowIndex = i / tl.ColumnCount;
-        int columnIndex = i % tl.ColumnCount;
-        tl.SetRow(control, rowIndex);
-        tl.SetColumn(control, columnIndex);
-      }
-      for (int i = 0; i < tl.RowCount; ++i) {
-        tl.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight));
-      }
+        tl.Controls.Remove(chartsPanel);
+      }
+
+      if (tl.Controls.Count > 0) {
+        SetupYAxis();
+
+        ReOrderControls();
+
+        // set column styles
+        int numVariables = VisibleVariables.Count();
+        tl.ColumnCount = Math.Min(numVariables, MaxColumns);
+        tl.ColumnStyles.Clear();
+        for (int c = 0; c < tl.ColumnCount; c++)
+          tl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100.0f / tl.ColumnCount));
+
+        // set column styles
+        tl.RowCount = (int)Math.Ceiling((double)numVariables / tl.ColumnCount);
+        var columnWidth = tl.Width / tl.ColumnCount; // assume all columns have the same width
+        var rowHeight = (int)(0.8 * columnWidth);
+        tl.RowStyles.Clear();
+        for (int r = 0; r < tl.RowCount; r++)
+          tl.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight));
+      }
+
       tl.ResumeLayout();
     }
@@ -204,4 +269,53 @@
       SetupYAxis();
     }
+
+    private void densityComboBox_SelectedIndexChanged(object sender, EventArgs e) {
+      int si = densityComboBox.SelectedIndex;
+      if (si == 0) {
+        foreach (var densityChart in densityCharts.Values)
+          densityChart.Visible = false;
+      } else {
+        var indices = GetDensityIndices(si).ToList();
+
+        foreach (var entry in densityCharts) {
+          var variableName = entry.Key;
+          var densityChart = entry.Value;
+          if (!VisibleVariables.Contains(variableName))
+            continue;
+          UpdateDensityChart(densityChart, variableName, indices);
+
+        }
+      }
+    }
+    private IEnumerable<int> GetDensityIndices(int selectedIndex) {
+      var problemData = Content.ProblemData;
+      return
+        selectedIndex == 1 ? problemData.TrainingIndices :
+        selectedIndex == 2 ? problemData.TestIndices :
+        problemData.AllIndices;
+    }
+    private void UpdateDensityChart(DensityChart densityChart, string variable, IList<int> indices = null) {
+      if (densityComboBox.SelectedIndex == 0)
+        return;
+      if (indices == null) {
+        indices = GetDensityIndices(densityComboBox.SelectedIndex).ToList();
+      }
+      var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
+      var gradientChart = gradientCharts[variable];
+      var min = gradientChart.FixedXAxisMin;
+      var max = gradientChart.FixedXAxisMax;
+      var buckets = gradientChart.DrawingSteps;
+      if (min.HasValue && max.HasValue) {
+        densityChart.UpdateChart(data, min.Value, max.Value, buckets);
+        densityChart.Width = gradientChart.Width;
+
+        var gcPlotPosition = gradientChart.InnerPlotPosition;
+        densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
+        densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
+
+        densityChart.Visible = true;
+      }
+    }
   }
 }
+
