source: branches/HeuristicLab.RegressionSolutionGradientView/HeuristicLab.Problems.DataAnalysis.Views/3.4/GradientChart.cs @ 14014

Last change on this file since 14014 was 14014, checked in by bburlacu, 6 years ago

#2597: Fixed small issue with layout update when the number of columns was changed. Improved layout of the configuration panel. Made GradientView invisible as an item in the Solution View.

File size: 25.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
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;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Globalization;
26using System.Linq;
27using System.Threading;
28using System.Threading.Tasks;
29using System.Windows.Forms;
30using System.Windows.Forms.DataVisualization.Charting;
31using HeuristicLab.Common;
32using HeuristicLab.MainForm.WindowsForms;
33using HeuristicLab.Visualization.ChartControlsExtensions;
34
35namespace HeuristicLab.Problems.DataAnalysis.Views {
36  public partial class GradientChart : UserControl {
37    private ModifiableDataset sharedFixedVariables; // used for syncronising variable values between charts
38    private ModifiableDataset internalDataset; // holds the x values for each point drawn
39
40    private CancellationTokenSource cancelCurrentRecalculateSource;
41
42    private readonly List<IRegressionSolution> solutions;
43    private readonly Dictionary<IRegressionSolution, Series> seriesCache;
44    private readonly Dictionary<IRegressionSolution, Series> ciSeriesCache;
45
46    private readonly ToolStripMenuItem configToolStripMenuItem;
47    private readonly GradientChartConfigurationDialog configurationDialog;
48
49    #region Properties
50    public string XAxisTitle {
51      get { return chart.ChartAreas[0].AxisX.Title; }
52      set { chart.ChartAreas[0].AxisX.Title = value; }
53    }
54
55    public string YAxisTitle {
56      get { return chart.ChartAreas[0].AxisY.Title; }
57      set { chart.ChartAreas[0].AxisY.Title = value; }
58    }
59
60    public bool ShowLegend {
61      get { return chart.Legends[0].Enabled; }
62      set { chart.Legends[0].Enabled = value; }
63    }
64    public bool ShowCursor {
65      get { return chart.Annotations[0].Visible; }
66      set {
67        chart.Annotations[0].Visible = value;
68        if (!value) chart.ChartAreas[0].AxisX.Title = string.Empty;
69      }
70    }
71
72    public bool ShowConfigButton {
73      get { return configurationButton.Visible; }
74      set { configurationButton.Visible = value; }
75    }
76
77    private int xAxisTicks = 5;
78    public int XAxisTicks {
79      get { return xAxisTicks; }
80      set {
81        if (value != xAxisTicks) {
82          xAxisTicks = value;
83          SetupAxis(chart.ChartAreas[0].AxisX, trainingMin, trainingMax, XAxisTicks, FixedXAxisMin, FixedXAxisMax);
84          RecalculateInternalDataset();
85        }
86      }
87    }
88    private double? fixedXAxisMin;
89    public double? FixedXAxisMin {
90      get { return fixedXAxisMin; }
91      set {
92        if ((value.HasValue && fixedXAxisMin.HasValue && !value.Value.IsAlmost(fixedXAxisMin.Value)) || (value.HasValue != fixedXAxisMin.HasValue)) {
93          fixedXAxisMin = value;
94          if (trainingMin < trainingMax) {
95            SetupAxis(chart.ChartAreas[0].AxisX, trainingMin, trainingMax, XAxisTicks, FixedXAxisMin, FixedXAxisMax);
96            RecalculateInternalDataset();
97            // set the vertical line position
98            if (VerticalLineAnnotation.X <= fixedXAxisMin) {
99              var axisX = chart.ChartAreas[0].AxisX;
100              var step = (axisX.Maximum - axisX.Minimum) / drawingSteps;
101              VerticalLineAnnotation.X = axisX.Minimum + step;
102            }
103          }
104        }
105      }
106    }
107    private double? fixedXAxisMax;
108    public double? FixedXAxisMax {
109      get { return fixedXAxisMax; }
110      set {
111        if ((value.HasValue && fixedXAxisMax.HasValue && !value.Value.IsAlmost(fixedXAxisMax.Value)) || (value.HasValue != fixedXAxisMax.HasValue)) {
112          fixedXAxisMax = value;
113          if (trainingMin < trainingMax) {
114            SetupAxis(chart.ChartAreas[0].AxisX, trainingMin, trainingMax, XAxisTicks, FixedXAxisMin, FixedXAxisMax);
115            RecalculateInternalDataset();
116            // set the vertical line position
117            if (VerticalLineAnnotation.X >= fixedXAxisMax) {
118              var axisX = chart.ChartAreas[0].AxisX;
119              var step = (axisX.Maximum - axisX.Minimum) / drawingSteps;
120              VerticalLineAnnotation.X = axisX.Maximum - step;
121            }
122          }
123        }
124      }
125    }
126
127    private int yAxisTicks = 5;
128    public int YAxisTicks {
129      get { return yAxisTicks; }
130      set {
131        if (value != yAxisTicks) {
132          yAxisTicks = value;
133          SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
134          RecalculateInternalDataset();
135        }
136      }
137    }
138    private double? fixedYAxisMin;
139    public double? FixedYAxisMin {
140      get { return fixedYAxisMin; }
141      set {
142        if ((value.HasValue && fixedYAxisMin.HasValue && !value.Value.IsAlmost(fixedYAxisMin.Value)) || (value.HasValue != fixedYAxisMin.HasValue)) {
143          fixedYAxisMin = value;
144          // the check below prevents an exception when yMin >= yMax
145          if (yMin < yMax)
146            SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
147        }
148      }
149    }
150    private double? fixedYAxisMax;
151    public double? FixedYAxisMax {
152      get { return fixedYAxisMax; }
153      set {
154        if ((value.HasValue && fixedYAxisMax.HasValue && !value.Value.IsAlmost(fixedYAxisMax.Value)) || (value.HasValue != fixedYAxisMax.HasValue)) {
155          fixedYAxisMax = value;
156          // the check below prevents an exception when yMin >= yMax
157          if (yMin < yMax)
158            SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
159        }
160      }
161    }
162
163    private double trainingMin = 1;
164    private double trainingMax = -1;
165
166    private int drawingSteps = 1000;
167    public int DrawingSteps {
168      get { return drawingSteps; }
169      set {
170        if (value != drawingSteps) {
171          drawingSteps = value;
172          RecalculateInternalDataset();
173          ResizeAllSeriesData();
174        }
175      }
176    }
177
178    private string freeVariable;
179    public string FreeVariable {
180      get { return freeVariable; }
181      set {
182        if (value == freeVariable) return;
183        if (solutions.Any(s => !s.ProblemData.Dataset.DoubleVariables.Contains(value))) {
184          throw new ArgumentException("Variable does not exist in the ProblemData of the Solutions.");
185        }
186        freeVariable = value;
187        RecalculateInternalDataset();
188      }
189    }
190
191    private double yMin;
192    public double YMin {
193      get { return yMin; }
194    }
195    private double yMax;
196    public double YMax {
197      get { return yMax; }
198    }
199
200    private VerticalLineAnnotation VerticalLineAnnotation {
201      get { return (VerticalLineAnnotation)chart.Annotations.SingleOrDefault(x => x is VerticalLineAnnotation); }
202    }
203
204    internal ElementPosition InnerPlotPosition {
205      get { return chart.ChartAreas[0].InnerPlotPosition; }
206    }
207    #endregion
208
209    public GradientChart() {
210      InitializeComponent();
211
212      solutions = new List<IRegressionSolution>();
213      seriesCache = new Dictionary<IRegressionSolution, Series>();
214      ciSeriesCache = new Dictionary<IRegressionSolution, Series>();
215
216      // Configure axis
217      chart.CustomizeAllChartAreas();
218      chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
219      chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
220      chart.ChartAreas[0].CursorX.Interval = 0;
221
222      chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
223      chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
224      chart.ChartAreas[0].CursorY.Interval = 0;
225
226      configToolStripMenuItem = new ToolStripMenuItem("Configuration");
227      configToolStripMenuItem.Click += config_Click;
228      chart.ContextMenuStrip.Items.Add(new ToolStripSeparator());
229      chart.ContextMenuStrip.Items.Add(configToolStripMenuItem);
230      configurationDialog = new GradientChartConfigurationDialog(this);
231
232      Disposed += GradientChart_Disposed;
233    }
234
235    private void GradientChart_Disposed(object sender, EventArgs e) {
236      if (cancelCurrentRecalculateSource != null)
237        cancelCurrentRecalculateSource.Cancel();
238    }
239
240    public void Configure(IEnumerable<IRegressionSolution> solutions, ModifiableDataset sharedFixedVariables, string freeVariable, int drawingSteps, bool initializeAxisRanges = true) {
241      if (!SolutionsCompatible(solutions))
242        throw new ArgumentException("Solutions are not compatible with the problem data.");
243      this.freeVariable = freeVariable;
244      this.drawingSteps = drawingSteps;
245
246      this.solutions.Clear();
247      this.solutions.AddRange(solutions);
248
249      // add an event such that whenever a value is changed in the shared dataset,
250      // this change is reflected in the internal dataset (where the value becomes a whole column)
251      if (this.sharedFixedVariables != null)
252        this.sharedFixedVariables.ItemChanged -= sharedFixedVariables_ItemChanged;
253      this.sharedFixedVariables = sharedFixedVariables;
254      this.sharedFixedVariables.ItemChanged += sharedFixedVariables_ItemChanged;
255
256      RecalculateTrainingLimits(initializeAxisRanges);
257      RecalculateInternalDataset();
258
259      chart.Series.Clear();
260      seriesCache.Clear();
261      ciSeriesCache.Clear();
262      foreach (var solution in this.solutions) {
263        var series = CreateSeries(solution);
264        seriesCache.Add(solution, series.Item1);
265        if (series.Item2 != null)
266          ciSeriesCache.Add(solution, series.Item2);
267      }
268
269      ResizeAllSeriesData();
270      OrderAndColorSeries();
271    }
272
273    public async Task RecalculateAsync(bool updateOnFinish = true, bool resetYAxis = true) {
274      if (IsDisposed
275        || sharedFixedVariables == null || !solutions.Any() || string.IsNullOrEmpty(freeVariable)
276        || trainingMin.IsAlmost(trainingMax) || trainingMin > trainingMax || drawingSteps == 0)
277        return;
278
279      calculationPendingTimer.Start();
280
281      Update(); // immediately show label
282
283      // cancel previous recalculate call
284      if (cancelCurrentRecalculateSource != null)
285        cancelCurrentRecalculateSource.Cancel();
286      cancelCurrentRecalculateSource = new CancellationTokenSource();
287
288      // Set cursor and x-axis
289      // Make sure to allow a small offset to be able to distinguish the vertical line annotation from the axis
290      var defaultValue = sharedFixedVariables.GetDoubleValue(freeVariable, 0);
291      var step = (trainingMax - trainingMin) / drawingSteps;
292      var minimum = chart.ChartAreas[0].AxisX.Minimum;
293      var maximum = chart.ChartAreas[0].AxisX.Maximum;
294      if (defaultValue <= minimum)
295        VerticalLineAnnotation.X = minimum + step;
296      else if (defaultValue >= maximum)
297        VerticalLineAnnotation.X = maximum - step;
298      else
299        VerticalLineAnnotation.X = defaultValue;
300
301      if (ShowCursor)
302        chart.ChartAreas[0].AxisX.Title = FreeVariable + " : " + defaultValue.ToString("N3", CultureInfo.CurrentCulture);
303
304      // Update series
305      var cancellationToken = cancelCurrentRecalculateSource.Token;
306      try {
307        var limits = await UpdateAllSeriesDataAsync(cancellationToken);
308        //cancellationToken.ThrowIfCancellationRequested();
309
310        yMin = limits.Lower;
311        yMax = limits.Upper;
312        // Set y-axis
313        if (resetYAxis)
314          SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
315
316        UpdateOutOfTrainingRangeStripLines();
317
318        calculationPendingTimer.Stop();
319        calculationPendingLabel.Visible = false;
320        if (updateOnFinish)
321          Update();
322      }
323      catch (OperationCanceledException) { }
324      catch (AggregateException ae) {
325        if (!ae.InnerExceptions.Any(e => e is OperationCanceledException))
326          throw;
327      }
328    }
329
330    private void SetupAxis(Axis axis, double minValue, double maxValue, int ticks, double? fixedAxisMin, double? fixedAxisMax) {
331      double axisMin, axisMax, axisInterval;
332      ChartUtil.CalculateAxisInterval(minValue, maxValue, ticks, out axisMin, out axisMax, out axisInterval);
333      axis.Minimum = fixedAxisMin ?? axisMin;
334      axis.Maximum = fixedAxisMax ?? axisMax;
335      axis.Interval = (axis.Maximum - axis.Minimum) / ticks;
336
337      try {
338        chart.ChartAreas[0].RecalculateAxesScale();
339      }
340      catch (InvalidOperationException) {
341        // Can occur if eg. axis min == axis max
342      }
343    }
344
345    private void RecalculateTrainingLimits(bool initializeAxisRanges) {
346      trainingMin = solutions.Select(s => s.ProblemData.Dataset.GetDoubleValues(freeVariable, s.ProblemData.TrainingIndices).Min()).Max();
347      trainingMax = solutions.Select(s => s.ProblemData.Dataset.GetDoubleValues(freeVariable, s.ProblemData.TrainingIndices).Max()).Min();
348
349      if (initializeAxisRanges) {
350        double xmin, xmax, xinterval;
351        ChartUtil.CalculateAxisInterval(trainingMin, trainingMax, XAxisTicks, out xmin, out xmax, out xinterval);
352        FixedXAxisMin = xmin;
353        FixedXAxisMax = xmax;
354      }
355    }
356
357    private void RecalculateInternalDataset() {
358      if (sharedFixedVariables == null)
359        return;
360
361      // we expand the range in order to get nice tick intervals on the x axis
362      double xmin, xmax, xinterval;
363      ChartUtil.CalculateAxisInterval(trainingMin, trainingMax, XAxisTicks, out xmin, out xmax, out xinterval);
364
365      if (FixedXAxisMin.HasValue) xmin = FixedXAxisMin.Value;
366      if (FixedXAxisMax.HasValue) xmax = FixedXAxisMax.Value;
367      double step = (xmax - xmin) / drawingSteps;
368
369      var xvalues = new List<double>();
370      for (int i = 0; i < drawingSteps; i++)
371        xvalues.Add(xmin + i * step);
372
373      var variables = sharedFixedVariables.DoubleVariables.ToList();
374      internalDataset = new ModifiableDataset(variables,
375        variables.Select(x => x == FreeVariable
376          ? xvalues
377          : Enumerable.Repeat(sharedFixedVariables.GetDoubleValue(x, 0), xvalues.Count).ToList()
378        )
379      );
380    }
381
382    private Tuple<Series, Series> CreateSeries(IRegressionSolution solution) {
383      var series = new Series {
384        ChartType = SeriesChartType.Line,
385        Name = solution.ProblemData.TargetVariable + " " + solutions.IndexOf(solution)
386      };
387      series.LegendText = series.Name;
388
389      var confidenceBoundSolution = solution as IConfidenceBoundRegressionSolution;
390      Series confidenceIntervalSeries = null;
391      if (confidenceBoundSolution != null) {
392        confidenceIntervalSeries = new Series {
393          ChartType = SeriesChartType.Range,
394          YValuesPerPoint = 2,
395          Name = "95% Conf. Interval " + series.Name,
396          IsVisibleInLegend = false
397        };
398      }
399      return Tuple.Create(series, confidenceIntervalSeries);
400    }
401
402    private void OrderAndColorSeries() {
403      chart.SuspendRepaint();
404
405      chart.Series.Clear();
406      // Add mean series for applying palette colors
407      foreach (var solution in solutions) {
408        chart.Series.Add(seriesCache[solution]);
409      }
410
411      chart.Palette = ChartColorPalette.BrightPastel;
412      chart.ApplyPaletteColors();
413      chart.Palette = ChartColorPalette.None;
414
415      // Add confidence interval series before its coresponding series for correct z index
416      foreach (var solution in solutions) {
417        Series ciSeries;
418        if (ciSeriesCache.TryGetValue(solution, out ciSeries)) {
419          var series = seriesCache[solution];
420          ciSeries.Color = Color.FromArgb(40, series.Color);
421          int idx = chart.Series.IndexOf(seriesCache[solution]);
422          chart.Series.Insert(idx, ciSeries);
423        }
424      }
425
426      chart.ResumeRepaint(true);
427    }
428
429    private async Task<DoubleLimit> UpdateAllSeriesDataAsync(CancellationToken cancellationToken) {
430      var updateTasks = solutions.Select(solution => UpdateSeriesDataAsync(solution, cancellationToken));
431
432      double min = double.MaxValue, max = double.MinValue;
433      foreach (var update in updateTasks) {
434        var limit = await update;
435        if (limit.Lower < min) min = limit.Lower;
436        if (limit.Upper > max) max = limit.Upper;
437      }
438
439      return new DoubleLimit(min, max);
440    }
441
442    private Task<DoubleLimit> UpdateSeriesDataAsync(IRegressionSolution solution, CancellationToken cancellationToken) {
443      return Task.Run(() => {
444        var xvalues = internalDataset.GetDoubleValues(FreeVariable).ToList();
445        var yvalues = solution.Model.GetEstimatedValues(internalDataset, Enumerable.Range(0, internalDataset.Rows)).ToList();
446
447        double min = double.MaxValue, max = double.MinValue;
448
449        var series = seriesCache[solution];
450        for (int i = 0; i < xvalues.Count; i++) {
451          series.Points[i].SetValueXY(xvalues[i], yvalues[i]);
452          if (yvalues[i] < min) min = yvalues[i];
453          if (yvalues[i] > max) max = yvalues[i];
454        }
455
456        var confidenceBoundSolution = solution as IConfidenceBoundRegressionSolution;
457        if (confidenceBoundSolution != null) {
458          var confidenceIntervalSeries = ciSeriesCache[solution];
459
460          cancellationToken.ThrowIfCancellationRequested();
461          var variances =
462            confidenceBoundSolution.Model.GetEstimatedVariances(internalDataset,
463              Enumerable.Range(0, internalDataset.Rows)).ToList();
464          for (int i = 0; i < xvalues.Count; i++) {
465            var lower = yvalues[i] - 1.96 * Math.Sqrt(variances[i]);
466            var upper = yvalues[i] + 1.96 * Math.Sqrt(variances[i]);
467            confidenceIntervalSeries.Points[i].SetValueXY(xvalues[i], lower, upper);
468            if (lower < min) min = lower;
469            if (upper > max) max = upper;
470          }
471        }
472
473        cancellationToken.ThrowIfCancellationRequested();
474        return new DoubleLimit(min, max);
475      }, cancellationToken);
476    }
477
478    private void ResizeAllSeriesData() {
479      if (internalDataset == null)
480        return;
481
482      var xvalues = internalDataset.GetDoubleValues(FreeVariable).ToList();
483      foreach (var solution in solutions)
484        ResizeSeriesData(solution, xvalues);
485    }
486    private void ResizeSeriesData(IRegressionSolution solution, IList<double> xvalues = null) {
487      if (xvalues == null)
488        xvalues = internalDataset.GetDoubleValues(FreeVariable).ToList();
489
490      var series = seriesCache[solution];
491      series.Points.SuspendUpdates();
492      series.Points.Clear();
493      for (int i = 0; i < xvalues.Count; i++)
494        series.Points.Add(new DataPoint(xvalues[i], 0.0));
495      series.Points.ResumeUpdates();
496
497      Series confidenceIntervalSeries;
498      if (ciSeriesCache.TryGetValue(solution, out confidenceIntervalSeries)) {
499        confidenceIntervalSeries.Points.SuspendUpdates();
500        confidenceIntervalSeries.Points.Clear();
501        for (int i = 0; i < xvalues.Count; i++)
502          confidenceIntervalSeries.Points.Add(new DataPoint(xvalues[i], new[] { -1.0, 1.0 }));
503        confidenceIntervalSeries.Points.ResumeUpdates();
504      }
505    }
506
507    public async Task AddSolutionAsync(IRegressionSolution solution) {
508      if (!SolutionsCompatible(solutions.Concat(new[] { solution })))
509        throw new ArgumentException("The solution is not compatible with the problem data.");
510      if (solutions.Contains(solution))
511        return;
512
513      solutions.Add(solution);
514      RecalculateTrainingLimits(true);
515
516      var series = CreateSeries(solution);
517      seriesCache.Add(solution, series.Item1);
518      if (series.Item2 != null)
519        ciSeriesCache.Add(solution, series.Item2);
520
521      ResizeSeriesData(solution);
522      OrderAndColorSeries();
523
524      await RecalculateAsync();
525      var args = new EventArgs<IRegressionSolution>(solution);
526      OnSolutionAdded(this, args);
527    }
528
529    public async Task RemoveSolutionAsync(IRegressionSolution solution) {
530      if (!solutions.Remove(solution))
531        return;
532
533      RecalculateTrainingLimits(true);
534
535      seriesCache.Remove(solution);
536      ciSeriesCache.Remove(solution);
537
538      await RecalculateAsync();
539      var args = new EventArgs<IRegressionSolution>(solution);
540      OnSolutionRemoved(this, args);
541    }
542
543    private static bool SolutionsCompatible(IEnumerable<IRegressionSolution> solutions) {
544      foreach (var solution1 in solutions) {
545        var variables1 = solution1.ProblemData.Dataset.DoubleVariables;
546        foreach (var solution2 in solutions) {
547          if (solution1 == solution2)
548            continue;
549          var variables2 = solution2.ProblemData.Dataset.DoubleVariables;
550          if (!variables1.All(variables2.Contains))
551            return false;
552        }
553      }
554      return true;
555    }
556
557    private void UpdateOutOfTrainingRangeStripLines() {
558      var axisX = chart.ChartAreas[0].AxisX;
559      var lowerStripLine = axisX.StripLines[0];
560      var upperStripLine = axisX.StripLines[1];
561
562      lowerStripLine.IntervalOffset = axisX.Minimum;
563      lowerStripLine.StripWidth = Math.Abs(trainingMin - axisX.Minimum);
564
565      upperStripLine.IntervalOffset = trainingMax;
566      upperStripLine.StripWidth = Math.Abs(axisX.Maximum - trainingMax);
567    }
568
569    #region Events
570    public event EventHandler<EventArgs<IRegressionSolution>> SolutionAdded;
571    public void OnSolutionAdded(object sender, EventArgs<IRegressionSolution> args) {
572      var added = SolutionAdded;
573      if (added == null) return;
574      added(sender, args);
575    }
576
577    public event EventHandler<EventArgs<IRegressionSolution>> SolutionRemoved;
578    public void OnSolutionRemoved(object sender, EventArgs<IRegressionSolution> args) {
579      var removed = SolutionRemoved;
580      if (removed == null) return;
581      removed(sender, args);
582    }
583
584    public event EventHandler VariableValueChanged;
585    public void OnVariableValueChanged(object sender, EventArgs args) {
586      var changed = VariableValueChanged;
587      if (changed == null) return;
588      changed(sender, args);
589    }
590
591    private void sharedFixedVariables_ItemChanged(object o, EventArgs<int, int> e) {
592      if (o != sharedFixedVariables) return;
593      var variables = sharedFixedVariables.DoubleVariables.ToList();
594      var rowIndex = e.Value;
595      var columnIndex = e.Value2;
596
597      var variableName = variables[columnIndex];
598      if (variableName == FreeVariable) return;
599      var v = sharedFixedVariables.GetDoubleValue(variableName, rowIndex);
600      var values = new List<double>(Enumerable.Repeat(v, DrawingSteps));
601      internalDataset.ReplaceVariable(variableName, values);
602    }
603
604    private void chart_AnnotationPositionChanging(object sender, AnnotationPositionChangingEventArgs e) {
605      var step = (trainingMax - trainingMin) / drawingSteps;
606      double newLocation = step * (long)Math.Round(e.NewLocationX / step);
607      var axisX = chart.ChartAreas[0].AxisX;
608      if (newLocation >= axisX.Maximum)
609        newLocation = axisX.Maximum - step;
610      if (newLocation <= axisX.Minimum)
611        newLocation = axisX.Minimum + step;
612
613      e.NewLocationX = newLocation;
614      var annotation = VerticalLineAnnotation;
615      var x = annotation.X;
616      sharedFixedVariables.SetVariableValue(x, FreeVariable, 0);
617
618      if (ShowCursor) {
619        chart.ChartAreas[0].AxisX.Title = FreeVariable + " : " + x.ToString("N3", CultureInfo.CurrentCulture);
620        chart.Update();
621      }
622
623      OnVariableValueChanged(this, EventArgs.Empty);
624    }
625
626    private void chart_MouseMove(object sender, MouseEventArgs e) {
627      bool hitCursor = chart.HitTest(e.X, e.Y).ChartElementType == ChartElementType.Annotation;
628      chart.Cursor = hitCursor ? Cursors.VSplit : Cursors.Default;
629    }
630
631    private void chart_DragDrop(object sender, DragEventArgs e) {
632      var data = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
633      if (data != null) {
634        var solution = data as IRegressionSolution;
635        if (!solutions.Contains(solution))
636          AddSolutionAsync(solution);
637      }
638    }
639    private void chart_DragEnter(object sender, DragEventArgs e) {
640      if (!e.Data.GetDataPresent(HeuristicLab.Common.Constants.DragDropDataFormat)) return;
641      e.Effect = DragDropEffects.None;
642
643      var data = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
644      var regressionSolution = data as IRegressionSolution;
645      if (regressionSolution != null) {
646        e.Effect = DragDropEffects.Copy;
647      }
648    }
649
650    private void calculationPendingTimer_Tick(object sender, EventArgs e) {
651      calculationPendingLabel.Visible = true;
652      Update();
653    }
654
655    private void config_Click(object sender, EventArgs e) {
656      configurationDialog.ShowDialog(this);
657    }
658    #endregion
659  }
660}
Note: See TracBrowser for help on using the repository browser.