Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.DataPreprocessing.Views/3.4/PreprocessingDataTableView.cs @ 11700

Last change on this file since 11700 was 11171, checked in by ascheibe, 10 years ago

#2115 merged r11170 (copyright update) into trunk

File size: 36.3 KB
RevLine 
[2908]1#region License Information
2/* HeuristicLab
[11171]3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[2908]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
[4068]23using System.Collections.Generic;
24using System.Drawing;
[3703]25using System.Linq;
[4068]26using System.Windows.Forms;
[2908]27using System.Windows.Forms.DataVisualization.Charting;
[10804]28using HeuristicLab.Collections;
29using HeuristicLab.Core.Views;
[10803]30using HeuristicLab.DataPreprocessing.Implementations;
[10804]31using HeuristicLab.MainForm;
[10877]32using HeuristicLab.Analysis;
33using HeuristicLab.Analysis.Views;
[2908]34
[10914]35namespace HeuristicLab.DataPreprocessing.Views {
[10771]36  [View("Preprocessing DataTable View")]
[10803]37  [Content(typeof(PreprocessingDataTable), false)]
[10914]38  public partial class PreprocessingDataTableView : ItemView, IConfigureableView {
[5097]39    protected List<Series> invisibleSeries;
40    protected Dictionary<IObservableList<double>, DataRow> valuesRowsTable;
[10952]41    private event EventHandler chartDoubleClick;
[7244]42
[10914]43    public new PreprocessingDataTable Content {
[10803]44      get { return (PreprocessingDataTable)base.Content; }
[2908]45      set { base.Content = value; }
46    }
47
[10908]48    public IEnumerable<double> Classification { get; set; }
[10867]49
[10914]50    public PreprocessingDataTableView() {
[2908]51      InitializeComponent();
52      valuesRowsTable = new Dictionary<IObservableList<double>, DataRow>();
[3703]53      invisibleSeries = new List<Series>();
[4636]54      chart.CustomizeAllChartAreas();
55      chart.ChartAreas[0].CursorX.Interval = 1;
[2908]56    }
57
[6342]58    #region Event Handler Registration
[10914]59    protected override void DeregisterContentEvents() {
[2908]60      foreach (DataRow row in Content.Rows)
61        DeregisterDataRowEvents(row);
[4870]62      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
[2908]63      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
64      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
65      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
66      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
[10847]67
68      Content.SelectedRows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
69      Content.SelectedRows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
70      Content.SelectedRows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
71      Content.SelectedRows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
[2908]72      base.DeregisterContentEvents();
73    }
[10914]74    protected override void RegisterContentEvents() {
[2908]75      base.RegisterContentEvents();
[4870]76      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
[2908]77      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
78      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
79      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
80      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
[10847]81
82      Content.SelectedRows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsAdded);
83      Content.SelectedRows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsRemoved);
84      Content.SelectedRows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsReplaced);
85      Content.SelectedRows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_CollectionReset);
[2908]86    }
87
[10847]88
[6342]89    protected virtual void RegisterDataRowEvents(DataRow row) {
90      row.NameChanged += new EventHandler(Row_NameChanged);
91      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
92      valuesRowsTable.Add(row.Values, row);
93      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
94      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
95      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
96      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
97      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
98    }
[10914]99    protected virtual void DeregisterDataRowEvents(DataRow row) {
[6342]100      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
101      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
102      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
103      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
104      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
105      valuesRowsTable.Remove(row.Values);
106      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
107      row.NameChanged -= new EventHandler(Row_NameChanged);
108    }
109    #endregion
110
[10908]111    protected override void OnContentChanged() {
[2908]112      base.OnContentChanged();
[3703]113      invisibleSeries.Clear();
[4870]114      chart.ChartAreas[0].AxisX.Title = string.Empty;
115      chart.ChartAreas[0].AxisY.Title = string.Empty;
116      chart.ChartAreas[0].AxisY2.Title = string.Empty;
[2908]117      chart.Series.Clear();
[10914]118      if (Content != null) {
[10851]119
[10908]120        if (Classification != null)
[10867]121          chart.Titles[0].Text = Content.Name;
122
[7977]123        AddDataRows(Content.Rows);
[10803]124        AddSelectedDataRows(Content.SelectedRows);
[6342]125        ConfigureChartArea(chart.ChartAreas[0]);
126        RecalculateAxesScale(chart.ChartAreas[0]);
[2908]127      }
128    }
129
[10914]130    protected override void SetEnabledStateOfControls() {
[3904]131      base.SetEnabledStateOfControls();
[3454]132      chart.Enabled = Content != null;
133    }
134
[10914]135    public void ShowConfiguration() {
136      if (Content != null) {
137        using (var dialog = new DataTableVisualPropertiesDialog(Content)) {
[7244]138          dialog.ShowDialog(this);
139        }
[10914]140      } else MessageBox.Show("Nothing to configure.");
[10877]141    }
[10914]142
143    protected virtual void AddDataRows(IEnumerable<DataRow> rows) {
144      foreach (var row in rows) {
[7977]145        RegisterDataRowEvents(row);
[9258]146        var series = new Series(row.Name);
[7977]147        if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
148        else series.LegendText = row.Name;
149        ConfigureSeries(series, row);
150        FillSeriesWithRowValues(series, row);
[10867]151
[10908]152        if (Classification == null)
153          chart.Series.Add(series);
[7977]154      }
[10803]155
[6342]156      ConfigureChartArea(chart.ChartAreas[0]);
157      RecalculateAxesScale(chart.ChartAreas[0]);
158      UpdateYCursorInterval();
159    }
160
[10803]161    protected virtual void AddSelectedDataRows(IEnumerable<DataRow> rows) {
[10851]162
[10868]163      // only paint selection for line chart
[10914]164      if (rows.Count() > 0 && rows.ElementAt(0).VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Line) {
[10868]165        // add dummy series for selction entry in legend
166        if (rows.Count() > 0 && chart.Series.FindByName("(Selection)") == null) {
[10914]167          Series series = new Series("(Selection)");
168          series.IsVisibleInLegend = true;
169          series.Color = Color.Green;
170          series.BorderWidth = 1;
171          series.BorderDashStyle = ChartDashStyle.Solid;
172          series.BorderColor = Color.Empty;
173          series.ChartType = SeriesChartType.FastLine;
174          chart.Series.Add(series);
175        }
[10851]176
[10914]177        foreach (var row in rows) {
[10803]178          row.VisualProperties.IsVisibleInLegend = false;
[10851]179          row.VisualProperties.Color = Color.Green;
[10847]180          //add selected to name in order to avoid naming conflict
[10868]181          var series = new Series(row.Name + "(Selected)");
[10803]182          ConfigureSeries(series, row);
183          FillSeriesWithRowValues(series, row);
[10867]184
[10914]185          if (Classification == null)
[10908]186            chart.Series.Add(series);
[10914]187
188        }
[10803]189      }
190    }
191
[10847]192    protected virtual void RemoveSelectedDataRows(IEnumerable<DataRow> rows) {
[10851]193
[10868]194      // only remove selection for line chart
[10914]195      if (rows.Count() > 0 && rows.ElementAt(0).VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Line) {
196        //remove selection entry in legend
197        if (Content.SelectedRows.Count == 0) {
198          Series series = chart.Series["(Selection)"];
199          chart.Series.Remove(series);
200        }
[10851]201
[10914]202        foreach (var row in rows) {
203          Series series = chart.Series[row.Name + "(Selected)"];
204          chart.Series.Remove(series);
205        }
[10847]206      }
207    }
208
[7979]209    protected virtual void RemoveDataRows(IEnumerable<DataRow> rows) {
210      foreach (var row in rows) {
211        DeregisterDataRowEvents(row);
212        Series series = chart.Series[row.Name];
213        chart.Series.Remove(series);
214        if (invisibleSeries.Contains(series))
215          invisibleSeries.Remove(series);
216      }
217      RecalculateAxesScale(chart.ChartAreas[0]);
218    }
219
[10914]220    private void ConfigureSeries(Series series, DataRow row) {
[6342]221      RemoveCustomPropertyIfExists(series, "PointWidth");
222      series.BorderWidth = 1;
223      series.BorderDashStyle = ChartDashStyle.Solid;
224      series.BorderColor = Color.Empty;
225
226      if (row.VisualProperties.Color != Color.Empty)
227        series.Color = row.VisualProperties.Color;
228      else series.Color = Color.Empty;
[7126]229      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
[10804]230
[4644]231      switch (row.VisualProperties.ChartType) {
232        case DataRowVisualProperties.DataRowChartType.Line:
233          series.ChartType = SeriesChartType.FastLine;
[6342]234          series.BorderWidth = row.VisualProperties.LineWidth;
235          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
[4644]236          break;
237        case DataRowVisualProperties.DataRowChartType.Bars:
[6342]238          // Bar is incompatible with anything but Bar and StackedBar*
239          if (!chart.Series.Any(x => x.ChartType != SeriesChartType.Bar && x.ChartType != SeriesChartType.StackedBar && x.ChartType != SeriesChartType.StackedBar100))
240            series.ChartType = SeriesChartType.Bar;
[10914]241          else {
[6342]242            series.ChartType = SeriesChartType.FastPoint; //default
243            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
244          }
[4644]245          break;
246        case DataRowVisualProperties.DataRowChartType.Columns:
247          series.ChartType = SeriesChartType.Column;
248          break;
249        case DataRowVisualProperties.DataRowChartType.Points:
250          series.ChartType = SeriesChartType.FastPoint;
251          break;
[6342]252        case DataRowVisualProperties.DataRowChartType.Histogram:
[10867]253          series.ChartType = SeriesChartType.StackedColumn;
[6342]254          series.SetCustomProperty("PointWidth", "1");
255          if (!series.Color.IsEmpty && series.Color.GetBrightness() < 0.25)
256            series.BorderColor = Color.White;
257          else series.BorderColor = Color.Black;
258          break;
[7297]259        case DataRowVisualProperties.DataRowChartType.StepLine:
260          series.ChartType = SeriesChartType.StepLine;
261          series.BorderWidth = row.VisualProperties.LineWidth;
262          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
263          break;
[4644]264        default:
265          series.ChartType = SeriesChartType.FastPoint;
266          break;
267      }
268      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
[6342]269      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
[6628]270      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
271      else series.LegendText = row.Name;
[7304]272
273      string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
274                      ? "X"
275                      : Content.VisualProperties.XAxisTitle;
276      string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
277                            ? "Y"
278                            : Content.VisualProperties.YAxisTitle;
279      series.ToolTip =
280        series.LegendText + Environment.NewLine +
281        xAxisTitle + " = " + "#INDEX," + Environment.NewLine +
282        yAxisTitle + " = " + "#VAL";
[2908]283    }
[3703]284
[10914]285    private void ConfigureChartArea(ChartArea area) {
[6342]286      if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;
287      if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;
[7224]288      chart.Titles[0].Text = Content.VisualProperties.Title;
[5097]289
[6342]290      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
291      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
292      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
293
294      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX2.TitleFont = Content.VisualProperties.AxisTitleFont;
295      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
296      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
297
298      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
299      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
300      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
301
302      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY2.TitleFont = Content.VisualProperties.AxisTitleFont;
303      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
304      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
[9258]305
306      area.AxisX.IsLogarithmic = Content.VisualProperties.XAxisLogScale;
307      area.AxisX2.IsLogarithmic = Content.VisualProperties.SecondXAxisLogScale;
308      area.AxisY.IsLogarithmic = Content.VisualProperties.YAxisLogScale;
309      area.AxisY2.IsLogarithmic = Content.VisualProperties.SecondYAxisLogScale;
[6342]310    }
311
[10914]312    private void RecalculateAxesScale(ChartArea area) {
[6342]313      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
[10914]314      foreach (Axis a in area.Axes) {
[6342]315        a.Minimum = double.NaN;
316        a.Maximum = double.NaN;
317      }
318      area.RecalculateAxesScale();
319      area.AxisX.IsMarginVisible = false;
320      area.AxisX2.IsMarginVisible = false;
321
322      if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
323      if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
324      if (!Content.VisualProperties.SecondXAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMinimumFixedValue)) area.AxisX2.Minimum = Content.VisualProperties.SecondXAxisMinimumFixedValue;
325      if (!Content.VisualProperties.SecondXAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMaximumFixedValue)) area.AxisX2.Maximum = Content.VisualProperties.SecondXAxisMaximumFixedValue;
326      if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
327      if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
328      if (!Content.VisualProperties.SecondYAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMinimumFixedValue)) area.AxisY2.Minimum = Content.VisualProperties.SecondYAxisMinimumFixedValue;
329      if (!Content.VisualProperties.SecondYAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMaximumFixedValue)) area.AxisY2.Maximum = Content.VisualProperties.SecondYAxisMaximumFixedValue;
[6676]330      if (area.AxisX.Minimum >= area.AxisX.Maximum) area.AxisX.Maximum = area.AxisX.Minimum + 1;
331      if (area.AxisX2.Minimum >= area.AxisX2.Maximum) area.AxisX2.Maximum = area.AxisX2.Minimum + 1;
332      if (area.AxisY.Minimum >= area.AxisY.Maximum) area.AxisY.Maximum = area.AxisY.Minimum + 1;
333      if (area.AxisY2.Minimum >= area.AxisY2.Maximum) area.AxisY2.Maximum = area.AxisY2.Minimum + 1;
[6342]334    }
335
[10914]336    protected virtual void UpdateYCursorInterval() {
[6342]337      double interestingValuesRange = (
338        from series in chart.Series
339        where series.Enabled
340        let values = (from point in series.Points
341                      where !point.IsEmpty
342                      select point.YValues[0]).DefaultIfEmpty(1.0)
343        let range = values.Max() - values.Min()
344        where range > 0.0
345        select range
346        ).DefaultIfEmpty(1.0).Min();
[3703]347
348      double digits = (int)Math.Log10(interestingValuesRange) - 3;
349      double yZoomInterval = Math.Pow(10, digits);
350      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
351    }
352
[6342]353    #region Event Handlers
354    #region Content Event Handlers
[10914]355    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
[4870]356      if (InvokeRequired)
357        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
[10914]358      else {
[6342]359        ConfigureChartArea(chart.ChartAreas[0]);
360        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
[4870]361      }
362    }
[6342]363    #endregion
364    #region Rows Event Handlers
[10847]365
366    private void SelectedRows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
367      if (InvokeRequired)
368        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
369      else {
370        RemoveSelectedDataRows(e.OldItems);
371        AddSelectedDataRows(e.Items);
372      }
373    }
374
375    private void SelectedRows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
376      if (InvokeRequired)
377        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
378      else {
379        RemoveSelectedDataRows(e.OldItems);
380        AddSelectedDataRows(e.Items);
381      }
382    }
383
384    private void SelectedRows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
385      if (InvokeRequired)
386        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
387      else {
388        RemoveSelectedDataRows(e.Items);
389      }
390    }
391
392    private void SelectedRows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
393      if (InvokeRequired)
394        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
395      else {
396        AddSelectedDataRows(e.Items);
397      }
398    }
399
400
[2908]401    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
402      if (InvokeRequired)
403        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
[10914]404      else {
[7977]405        AddDataRows(e.Items);
[2908]406      }
407    }
[10914]408    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
[2908]409      if (InvokeRequired)
410        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
[10914]411      else {
[7977]412        RemoveDataRows(e.Items);
[2908]413      }
414    }
[10914]415    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
[2908]416      if (InvokeRequired)
417        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
[10914]418      else {
[7977]419        RemoveDataRows(e.OldItems);
420        AddDataRows(e.Items);
[2908]421      }
422    }
[10914]423    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
[2908]424      if (InvokeRequired)
425        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
[10914]426      else {
[7977]427        RemoveDataRows(e.OldItems);
428        AddDataRows(e.Items);
[2908]429      }
430    }
[6342]431    #endregion
432    #region Row Event Handlers
[10914]433    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
[4644]434      if (InvokeRequired)
435        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
[10914]436      else {
[4644]437        DataRow row = (DataRow)sender;
[6342]438        Series series = chart.Series[row.Name];
439        series.Points.Clear();
440        ConfigureSeries(series, row);
441        FillSeriesWithRowValues(series, row);
442        RecalculateAxesScale(chart.ChartAreas[0]);
[4644]443      }
444    }
[10914]445    private void Row_NameChanged(object sender, EventArgs e) {
[2908]446      if (InvokeRequired)
447        Invoke(new EventHandler(Row_NameChanged), sender, e);
[10914]448      else {
[2908]449        DataRow row = (DataRow)sender;
450        chart.Series[row.Name].Name = row.Name;
451      }
452    }
[6342]453    #endregion
454    #region Values Event Handlers
[10914]455    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
[2908]456      if (InvokeRequired)
457        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
[10914]458      else {
[3561]459        DataRow row = null;
460        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10914]461        if (row != null) {
[3703]462          Series rowSeries = chart.Series[row.Name];
[10914]463          if (!invisibleSeries.Contains(rowSeries)) {
[4778]464            rowSeries.Points.Clear();
465            FillSeriesWithRowValues(rowSeries, row);
[6342]466            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]467            UpdateYCursorInterval();
[3080]468          }
469        }
[2908]470      }
471    }
[10914]472    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
[2908]473      if (InvokeRequired)
474        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
[10914]475      else {
[3561]476        DataRow row = null;
477        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10914]478        if (row != null) {
[3703]479          Series rowSeries = chart.Series[row.Name];
[10914]480          if (!invisibleSeries.Contains(rowSeries)) {
[4778]481            rowSeries.Points.Clear();
482            FillSeriesWithRowValues(rowSeries, row);
[6342]483            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]484            UpdateYCursorInterval();
485          }
[3561]486        }
[2908]487      }
488    }
[10914]489    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
[2908]490      if (InvokeRequired)
491        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
[10914]492      else {
[3561]493        DataRow row = null;
494        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10914]495        if (row != null) {
[3703]496          Series rowSeries = chart.Series[row.Name];
[10914]497          if (!invisibleSeries.Contains(rowSeries)) {
498            if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram) {
[6342]499              rowSeries.Points.Clear();
500              FillSeriesWithRowValues(rowSeries, row);
[10914]501            } else {
502              foreach (IndexedItem<double> item in e.Items) {
[6342]503                if (IsInvalidValue(item.Value))
504                  rowSeries.Points[item.Index].IsEmpty = true;
[10914]505                else {
[6342]506                  rowSeries.Points[item.Index].YValues = new double[] { item.Value };
507                  rowSeries.Points[item.Index].IsEmpty = false;
508                }
[3703]509              }
[3561]510            }
[6342]511            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]512            UpdateYCursorInterval();
[3530]513          }
[3080]514        }
[2908]515      }
516    }
[10914]517    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
[2908]518      if (InvokeRequired)
519        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
[10914]520      else {
[3561]521        DataRow row = null;
522        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10914]523        if (row != null) {
[3703]524          Series rowSeries = chart.Series[row.Name];
[10914]525          if (!invisibleSeries.Contains(rowSeries)) {
[4778]526            rowSeries.Points.Clear();
527            FillSeriesWithRowValues(rowSeries, row);
[6342]528            RecalculateAxesScale(chart.ChartAreas[0]);
[3703]529            UpdateYCursorInterval();
[3530]530          }
[3080]531        }
[2908]532      }
533    }
[3530]534
[10914]535    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e) {
[2908]536      if (InvokeRequired)
537        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
[10914]538      else {
[3561]539        DataRow row = null;
540        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
[10914]541        if (row != null) {
[3703]542          Series rowSeries = chart.Series[row.Name];
[10914]543          if (!invisibleSeries.Contains(rowSeries)) {
[3703]544            rowSeries.Points.Clear();
[4778]545            FillSeriesWithRowValues(rowSeries, row);
[6342]546            RecalculateAxesScale(chart.ChartAreas[0]);
[4778]547            UpdateYCursorInterval();
[3530]548          }
[3080]549        }
[2908]550      }
551    }
552    #endregion
[6342]553    #endregion
[4623]554
[6342]555    #region Chart Event Handlers
[10914]556    private void chart_MouseDown(object sender, MouseEventArgs e) {
[3703]557      HitTestResult result = chart.HitTest(e.X, e.Y);
[10914]558      if (result.ChartElementType == ChartElementType.LegendItem) {
[3703]559        ToggleSeriesVisible(result.Series);
560      }
561    }
[10914]562    private void chart_MouseMove(object sender, MouseEventArgs e) {
[6342]563      HitTestResult result = chart.HitTest(e.X, e.Y);
564      if (result.ChartElementType == ChartElementType.LegendItem)
565        this.Cursor = Cursors.Hand;
566      else
567        this.Cursor = Cursors.Default;
568    }
[10914]569    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
570      foreach (LegendItem legendItem in e.LegendItems) {
[6342]571        var series = chart.Series[legendItem.SeriesName];
[10914]572        if (series != null) {
[6342]573          bool seriesIsInvisible = invisibleSeries.Contains(series);
[10914]574          foreach (LegendCell cell in legendItem.Cells) {
[6342]575            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
576          }
577        }
578      }
579    }
580    #endregion
[3530]581
[10914]582    private void ToggleSeriesVisible(Series series) {
[10871]583
584      if (!Content.Rows.Any(x => x.Name == series.Name))
585        return;
586
[10914]587      if (!invisibleSeries.Contains(series)) {
[3703]588        series.Points.Clear();
589        invisibleSeries.Add(series);
[10914]590      } else {
[3703]591        invisibleSeries.Remove(series);
[10914]592        if (Content != null) {
[3703]593
594          var row = (from r in Content.Rows
595                     where r.Name == series.Name
596                     select r).Single();
597          FillSeriesWithRowValues(series, row);
598          this.chart.Legends[series.Legend].ForeColor = Color.Black;
[6342]599          RecalculateAxesScale(chart.ChartAreas[0]);
[3703]600          UpdateYCursorInterval();
601        }
602      }
603    }
604
[10914]605    private void FillSeriesWithRowValues(Series series, DataRow row) {
606      switch (row.VisualProperties.ChartType) {
[6342]607        case DataRowVisualProperties.DataRowChartType.Histogram:
608          CalculateHistogram(series, row);
609          break;
[10914]610        default: {
[9258]611            bool yLogarithmic = series.YAxisType == AxisType.Primary
612                                  ? Content.VisualProperties.YAxisLogScale
613                                  : Content.VisualProperties.SecondYAxisLogScale;
614            bool xLogarithmic = series.XAxisType == AxisType.Primary
615                                  ? Content.VisualProperties.XAxisLogScale
616                                  : Content.VisualProperties.SecondXAxisLogScale;
[10914]617            for (int i = 0; i < row.Values.Count; i++) {
[6342]618              var value = row.Values[i];
[9258]619              var point = new DataPoint();
620              point.XValue = row.VisualProperties.StartIndexZero && !xLogarithmic ? i : i + 1;
621              if (IsInvalidValue(value) || (yLogarithmic && value <= 0))
[6342]622                point.IsEmpty = true;
623              else
624                point.YValues = new double[] { value };
625              series.Points.Add(point);
626            }
627          }
628          break;
[3703]629      }
630    }
631
[10803]632    // get minimum ignores nan values
[10804]633    private double GetMinimum(IEnumerable<double> values) {
[10803]634      double min = Double.MaxValue;
635
[10914]636      foreach (double value in values) {
[10803]637        if (!Double.IsNaN(value) && value < min)
[10804]638          min = value;
[10803]639      }
640      return min;
641    }
642
643    //get maximium ignores nan values
[10914]644    private double GetMaximum(IEnumerable<double> values) {
[10803]645      double max = Double.MinValue;
646
[10914]647      foreach (double value in values) {
[10803]648        if (!Double.IsNaN(value) && value > max)
649          max = value;
650      }
651      return max;
652    }
653
[10908]654    protected virtual void CalculateHistogram(Series series, DataRow row) {
655      if (Classification != null) {
[10867]656
[10908]657        var valuesPerClass = row.Values.Select((i, index) => new { i, j = Classification.ToList()[index] })
658                                       .GroupBy((x) => x.j)
659                                       .ToDictionary(x => x.Key, x => x.Select(v => v.i)
660                                       .ToList());
661
[10867]662        chart.Titles.Add(row.Name);
[10908]663
664        foreach (KeyValuePair<double, List<double>> entry in valuesPerClass) {
[10867]665          var s = new Series(row.Name + entry.Key);
666
667          ConfigureSeries(s, row);
668          AddPointsToHistogramSeries(s, row, entry.Value);
669
670          s.LegendText = entry.Key.ToString();
671
672          chart.Series.Add(s);
673        }
[10908]674      } else {
675        series.Points.Clear();
[10867]676        ConfigureSeries(series, row);
677        AddPointsToHistogramSeries(series, row, null);
678      }
679    }
680
[10908]681    private void AddPointsToHistogramSeries(Series series, DataRow row, List<double> values) {
[6342]682      if (!row.Values.Any()) return;
683      int bins = row.VisualProperties.Bins;
684
[10803]685      double minValue = GetMinimum(row.Values);
686      double maxValue = GetMaximum(row.Values);
[6342]687      double intervalWidth = (maxValue - minValue) / bins;
688      if (intervalWidth < 0) return;
[10914]689      if (intervalWidth == 0) {
[6342]690        series.Points.AddXY(minValue, row.Values.Count);
691        return;
692      }
693
[10847]694
[6342]695      if (!row.VisualProperties.ExactBins) {
696        intervalWidth = HumanRoundRange(intervalWidth);
697        minValue = Math.Floor(minValue / intervalWidth) * intervalWidth;
698        maxValue = Math.Ceiling(maxValue / intervalWidth) * intervalWidth;
699      }
700
[7223]701      double intervalCenter = intervalWidth / 2;
702
[8339]703      double min = 0.0, max = 0.0;
[6978]704      if (!Double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue) && !Content.VisualProperties.XAxisMinimumAuto)
[7223]705        min = Content.VisualProperties.XAxisMinimumFixedValue;
706      else min = minValue;
[8339]707      if (!Double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue) && !Content.VisualProperties.XAxisMaximumAuto)
708        max = Content.VisualProperties.XAxisMaximumFixedValue;
709      else max = maxValue + intervalWidth;
[6978]710
[7223]711      double axisInterval = intervalWidth / row.VisualProperties.ScaleFactor;
[6978]712
[7223]713      var area = chart.ChartAreas[0];
714      area.AxisX.Interval = axisInterval;
[6978]715
[7223]716      series.SetCustomProperty("PointWidth", "1"); // 0.8 is the default value
717
718      // get the range or intervals which define the grouping of the frequency values
[8339]719      var doubleRange = DoubleRange(min, max, intervalWidth).Skip(1).ToList();
[7223]720
[10867]721
722      if (values == null) {
723        values = row.Values.ToList(); ;
724      }
725
[7223]726      // aggregate the row values by unique key and frequency value
[10867]727      var valueFrequencies = (from v in values
[7223]728                              where !IsInvalidValue(v)
729                              orderby v
730                              group v by v into g
731                              select new Tuple<double, double>(g.First(), g.Count())).ToList();
732
[10867]733      //  shift the chart to the left so the bars are placed on the intervals
[10908]734      if (Classification != null || valueFrequencies.First().Item1 < doubleRange.First()) {
[7223]735        series.Points.Add(new DataPoint(min - intervalWidth, 0));
[10867]736        series.Points.Add(new DataPoint(max + intervalWidth, 0));
737      }
[7223]738
739      // add data points
740      int j = 0;
[10914]741      foreach (var d in doubleRange) {
[7223]742        double sum = 0.0;
743        // sum the frequency values that fall within the same interval
[10914]744        while (j < valueFrequencies.Count && valueFrequencies[j].Item1 < d) {
[7223]745          sum += valueFrequencies[j].Item2;
746          ++j;
[3703]747        }
[7304]748        string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
749                              ? "X"
750                              : Content.VisualProperties.XAxisTitle;
751        string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
752                              ? "Y"
753                              : Content.VisualProperties.YAxisTitle;
[10914]754        series.Points.Add(new DataPoint(d - intervalCenter, sum) {
[7304]755          ToolTip =
756            xAxisTitle + ": [" + (d - intervalWidth) + "-" + d + ")" + Environment.NewLine +
757            yAxisTitle + ": " + sum
758        });
[3703]759      }
760    }
761
[10952]762    public event EventHandler ChartDoubleClick {
763      add { chartDoubleClick += value; }
764      remove { chartDoubleClick -= value; }
765    }
766
[6342]767    #region Helpers
[10914]768    public static IEnumerable<double> DoubleRange(double min, double max, double step) {
[7223]769      double i;
770      for (i = min; i <= max; i += step)
771        yield return i;
772
773      if (i != max + step)
774        yield return i;
775    }
776
[10914]777    protected void RemoveCustomPropertyIfExists(Series series, string property) {
[6342]778      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
779    }
[5097]780
[10914]781    private double HumanRoundRange(double range) {
[6342]782      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
783      double rounding = range / base10;
784      if (rounding <= 1.5) rounding = 1;
785      else if (rounding <= 2.25) rounding = 2;
786      else if (rounding <= 3.75) rounding = 2.5;
787      else if (rounding <= 7.5) rounding = 5;
788      else rounding = 10;
789      return rounding * base10;
790    }
791
[10914]792    private double HumanRoundMax(double max) {
[6342]793      double base10;
794      if (max > 0) base10 = Math.Pow(10.0, Math.Floor(Math.Log10(max)));
795      else base10 = Math.Pow(10.0, Math.Ceiling(Math.Log10(-max)));
796      double rounding = (max > 0) ? base10 : -base10;
797      while (rounding < max) rounding += base10;
798      return rounding;
799    }
800
[10914]801    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle) {
802      switch (dataRowLineStyle) {
[6342]803        case DataRowVisualProperties.DataRowLineStyle.Dash:
804          return ChartDashStyle.Dash;
805        case DataRowVisualProperties.DataRowLineStyle.DashDot:
806          return ChartDashStyle.DashDot;
807        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
808          return ChartDashStyle.DashDotDot;
809        case DataRowVisualProperties.DataRowLineStyle.Dot:
810          return ChartDashStyle.Dot;
811        case DataRowVisualProperties.DataRowLineStyle.NotSet:
812          return ChartDashStyle.NotSet;
813        case DataRowVisualProperties.DataRowLineStyle.Solid:
814          return ChartDashStyle.Solid;
815        default:
816          return ChartDashStyle.NotSet;
817      }
818    }
819
[10914]820    protected static bool IsInvalidValue(double x) {
[3530]821      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
822    }
[6342]823    #endregion
[10952]824
825    //bubble double click event with data table view as sender
826    private void chart_DoubleClick(object sender, EventArgs e) {
827      if (chartDoubleClick != null)
828        chartDoubleClick(this, e);
829    }
[2908]830  }
831}
Note: See TracBrowser for help on using the repository browser.