Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
07/11/17 19:36:03 (7 years ago)
Author:
pfleck
Message:

#2592

  • Renamed ScatterPlotContent to ParetoFrontScatterPlot (also renamed corresponding view).
  • Refactored ParetoFrontScatterPlotView (use ScatterPlot internally).
File:
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Problems.TestFunctions.Views/3.3/ParetoFrontScatterPlotView.cs

    r15202 r15203  
    1919 */
    2020#endregion
     21
    2122using System;
    22 using System.Drawing;
    2323using System.Linq;
    24 using System.Text;
    25 using System.Windows.Forms;
    26 using System.Windows.Forms.DataVisualization.Charting;
     24using HeuristicLab.Analysis;
     25using HeuristicLab.Common;
    2726using HeuristicLab.Core.Views;
    2827using HeuristicLab.MainForm;
     
    3130namespace HeuristicLab.Problems.TestFunctions.Views {
    3231  [View("Scatter Plot")]
    33   [Content(typeof(ScatterPlotContent))]
    34   public partial class MultiObjectiveTestFunctionParetoFrontScatterPlotView : ItemView {
    35     private const string QUALITIES = "Qualities";
    36     private const string PARETO_FRONT = "Best Known Pareto Front";
    37     private Series qualitySeries;
    38     private Series paretoSeries;
    39     private int xDim = 0;
    40     private int yDim = 1;
    41     int objectives = -1;
     32  [Content(typeof(ParetoFrontScatterPlot))]
     33  public partial class ParetoFrontScatterPlotView : ItemView {
    4234
    43     public new ScatterPlotContent Content {
    44       get { return (ScatterPlotContent)base.Content; }
     35    private readonly ScatterPlot scatterPlot;
     36    private readonly ScatterPlotDataRow qualitiesRow;
     37    private readonly ScatterPlotDataRow paretoFrontRow;
     38
     39    private int oldObjectives = -1;
     40    private int oldProblemSize = -1;
     41
     42    private bool suppressEvents;
     43
     44    public new ParetoFrontScatterPlot Content {
     45      get { return (ParetoFrontScatterPlot)base.Content; }
    4546      set { base.Content = value; }
    4647    }
    4748
    48     public MultiObjectiveTestFunctionParetoFrontScatterPlotView()
    49       : base() {
     49    public ParetoFrontScatterPlotView() {
    5050      InitializeComponent();
    5151
    52       BuildEmptySeries();
     52      scatterPlot = new ScatterPlot();
    5353
    54       //start with qualities toggled ON
    55       qualitySeries.Points.AddXY(0, 0);
     54      qualitiesRow = new ScatterPlotDataRow("Qualities", string.Empty, Enumerable.Empty<Point2D<double>>()) {
     55        VisualProperties = {
     56          PointSize = 8 ,
     57          PointStyle = ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Circle
     58        }
     59      };
     60      scatterPlot.Rows.Add(qualitiesRow);
    5661
    57       this.chart.TextAntiAliasingQuality = TextAntiAliasingQuality.High;
    58       this.chart.AxisViewChanged += new EventHandler<System.Windows.Forms.DataVisualization.Charting.ViewEventArgs>(chart_AxisViewChanged);
    59       this.chart.GetToolTipText += new System.EventHandler<ToolTipEventArgs>(this.Chart_GetToolTipText);
    60 
    61       //configure axis
    62       this.chart.CustomizeAllChartAreas();
    63       this.chart.ChartAreas[0].AxisX.Title = "Objective " + xDim;
    64       this.chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
    65       this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
    66       this.chart.ChartAreas[0].CursorX.Interval = 1;
    67       this.chart.ChartAreas[0].CursorY.Interval = 1;
    68 
    69       this.chart.ChartAreas[0].AxisY.Title = "Objective " + yDim;
    70       this.chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
    71       this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
    72       this.chart.ChartAreas[0].AxisY.IsStartedFromZero = true;
    73     }
    74 
    75 
    76     private void Chart_GetToolTipText(object sender, ToolTipEventArgs e) {
    77       if (e.HitTestResult.ChartElementType == ChartElementType.LegendItem) {
    78         if (e.HitTestResult.Series == paretoSeries && (Content.ParetoFront == null || Content.ParetoFront.Length == 0)) {
    79           e.Text = "No optimal pareto front is available for this problem with this number of objectives";
    80         }
    81         if (e.HitTestResult.Series == paretoSeries && (xDim >= Content.Objectives || yDim >= Content.Objectives)) {
    82           e.Text = "The optimal pareto front can only be displayed in  Objective Space";
    83         }
    84       }
    85 
    86       // Check selected chart element and set tooltip text
    87       if (e.HitTestResult.ChartElementType == ChartElementType.DataPoint) {
    88         int i = e.HitTestResult.PointIndex;
    89         StringBuilder toolTippText = new StringBuilder();
    90         DataPoint qp = e.HitTestResult.Series.Points[i];
    91         toolTippText.Append("Objective " + xDim + " = " + qp.XValue + "\n");
    92         toolTippText.Append("Objective " + yDim + " = " + qp.YValues[0]);
    93 
    94         Series s = e.HitTestResult.Series;
    95         if (s.Equals(this.chart.Series[QUALITIES])) {
    96           double[] dp = Content.Solutions[i];
    97           toolTippText.Append("\nSolution: {");
    98           for (int j = 0; j < dp.Length; j++) {
    99             toolTippText.Append(dp[j]);
    100             toolTippText.Append(";");
     62      paretoFrontRow = new ScatterPlotDataRow("Best Known Pareto Front", string.Empty, Enumerable.Empty<Point2D<double>>()) {
     63        VisualProperties = {
     64            PointSize = 4,
     65            PointStyle = ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Square
    10166          }
    102           toolTippText.Remove(toolTippText.Length - 1, 1);
    103           toolTippText.Append("}");
    104           e.Text = toolTippText.ToString();
    105         }
    106 
    107 
    108       }
     67      };
     68      scatterPlot.Rows.Add(paretoFrontRow);
    10969    }
    11070
    11171    protected override void OnContentChanged() {
    11272      base.OnContentChanged();
    113       if (Content == null) return;
    114       if (objectives != Content.Objectives) {
    115         AddMenuItems();
    116         objectives = Content.Objectives;
     73
     74      if (Content == null) {
     75        scatterPlotView.Content = null;
     76        xAxisComboBox.Items.Clear();
     77        xAxisComboBox.SelectedIndex = -1;
     78        yAxisComboBox.Items.Clear();
     79        yAxisComboBox.SelectedIndex = -1;
     80        return;
    11781      }
    118       if (Content.ParetoFront == null && chart.Series.Contains(paretoSeries)) {
    119         Series s = this.chart.Series[PARETO_FRONT];
    120         paretoSeries = null;
    121         this.chart.Series.Remove(s);
    12282
    123       } else if (Content.ParetoFront != null && !chart.Series.Contains(paretoSeries)) {
    124         this.chart.Series.Add(PARETO_FRONT);
    125         paretoSeries = this.chart.Series[PARETO_FRONT];
    126         this.chart.Series[PARETO_FRONT].LegendText = PARETO_FRONT;
    127         this.chart.Series[PARETO_FRONT].ChartType = SeriesChartType.FastPoint;
    128       }
    129       UpdateChart();
     83      scatterPlotView.Content = scatterPlot;
     84
     85      if (oldObjectives != Content.Objectives || oldProblemSize != Content.ProblemSize)
     86        UpdateAxisComboBoxes();
     87
     88      UpdateChartData();
     89
     90      oldObjectives = Content.Objectives;
     91      oldProblemSize = Content.ProblemSize;
    13092    }
    13193
    132     private void UpdateChart() {
    133       if (InvokeRequired) Invoke((Action)UpdateChart);
    134       else {
    135         if (Content != null) {
    136           this.UpdateSeries();
    137           if (!this.chart.Series.Any(s => s.Points.Count > 0))
    138             this.ClearChart();
     94    private void UpdateChartData() {
     95      if (InvokeRequired) {
     96        Invoke((Action)UpdateChartData);
     97        return;
     98      }
     99
     100      int xDimGlobal = xAxisComboBox.SelectedIndex;
     101      int yDimGlobal = yAxisComboBox.SelectedIndex;
     102
     103      qualitiesRow.Points.Replace(CreatePoints(Content.Qualities, Content.Solutions, xDimGlobal, yDimGlobal));
     104
     105      paretoFrontRow.Points.Replace(CreatePoints(Content.ParetoFront, null, xDimGlobal, yDimGlobal));
     106      paretoFrontRow.VisualProperties.IsVisibleInLegend = paretoFrontRow.Points.Count > 0; // hide if empty
     107    }
     108
     109    private void UpdateAxisComboBoxes() {
     110      try {
     111        suppressEvents = true;
     112
     113        string prevSelectedX = (string)xAxisComboBox.SelectedItem;
     114        string prevSelectedY = (string)yAxisComboBox.SelectedItem;
     115
     116        xAxisComboBox.Items.Clear();
     117        yAxisComboBox.Items.Clear();
     118
     119        // Add Objectives first
     120        for (int i = 0; i < Content.Objectives; i++) {
     121          xAxisComboBox.Items.Add("Objective " + i);
     122          yAxisComboBox.Items.Add("Objective " + i);
    139123        }
     124
     125        // Add Problem Dimension
     126        for (int i = 0; i < Content.ProblemSize; i++) {
     127          xAxisComboBox.Items.Add("Problem Dimension " + i);
     128          yAxisComboBox.Items.Add("Problem Dimension " + i);
     129        }
     130
     131        // Selection
     132        int count = xAxisComboBox.Items.Count;
     133        if (count > 0) {
     134          if (prevSelectedX != null && xAxisComboBox.Items.Contains(prevSelectedX))
     135            xAxisComboBox.SelectedItem = prevSelectedX;
     136          else xAxisComboBox.SelectedIndex = 0;
     137
     138          if (prevSelectedY != null && yAxisComboBox.Items.Contains(prevSelectedY))
     139            yAxisComboBox.SelectedItem = prevSelectedY;
     140          else yAxisComboBox.SelectedIndex = Math.Min(1, count - 1);
     141        } else {
     142          xAxisComboBox.SelectedIndex = -1;
     143          yAxisComboBox.SelectedIndex = -1;
     144        }
     145
     146        UpdateAxisDescription();
     147      } finally {
     148        suppressEvents = false;
    140149      }
    141150    }
    142151
    143     private void UpdateCursorInterval() {
    144       var estimatedValues = this.chart.Series[QUALITIES].Points.Select(x => x.XValue).DefaultIfEmpty(1.0);
    145       var targetValues = this.chart.Series[QUALITIES].Points.Select(x => x.YValues[0]).DefaultIfEmpty(1.0);
    146       double estimatedValuesRange = estimatedValues.Max() - estimatedValues.Min();
    147       double targetValuesRange = targetValues.Max() - targetValues.Min();
    148       double interestingValuesRange = Math.Min(Math.Max(targetValuesRange, 1.0), Math.Max(estimatedValuesRange, 1.0));
    149       double digits = (int)Math.Log10(interestingValuesRange) - 3;
    150       double zoomInterval = Math.Max(Math.Pow(10, digits), 10E-5);
    151       this.chart.ChartAreas[0].CursorX.Interval = zoomInterval;
    152       this.chart.ChartAreas[0].CursorY.Interval = zoomInterval;
    153 
    154       this.chart.ChartAreas[0].AxisX.ScaleView.SmallScrollSize = zoomInterval;
    155       this.chart.ChartAreas[0].AxisY.ScaleView.SmallScrollSize = zoomInterval;
    156 
    157       this.chart.ChartAreas[0].AxisX.ScaleView.SmallScrollMinSizeType = DateTimeIntervalType.Number;
    158       this.chart.ChartAreas[0].AxisX.ScaleView.SmallScrollMinSize = zoomInterval;
    159       this.chart.ChartAreas[0].AxisY.ScaleView.SmallScrollMinSizeType = DateTimeIntervalType.Number;
    160       this.chart.ChartAreas[0].AxisY.ScaleView.SmallScrollMinSize = zoomInterval;
    161 
    162       if (digits < 0) {
    163         this.chart.ChartAreas[0].AxisX.LabelStyle.Format = "F" + (int)Math.Abs(digits);
    164         this.chart.ChartAreas[0].AxisY.LabelStyle.Format = "F" + (int)Math.Abs(digits);
    165       } else {
    166         this.chart.ChartAreas[0].AxisX.LabelStyle.Format = "F0";
    167         this.chart.ChartAreas[0].AxisY.LabelStyle.Format = "F0";
    168       }
     152    private void UpdateAxisDescription() {
     153      scatterPlot.VisualProperties.XAxisTitle = (string)xAxisComboBox.SelectedItem;
     154      scatterPlot.VisualProperties.YAxisTitle = (string)yAxisComboBox.SelectedItem;
    169155    }
    170156
    171     private void UpdateSeries() {
    172       if (InvokeRequired) Invoke((Action)UpdateSeries);
    173       else {
     157    private static Point2D<double>[] CreatePoints(double[][] qualities, double[][] solutions, int xDimGlobal, int yDimGlobal) {
     158      if (qualities == null || qualities.Length == 0) return new Point2D<double>[0];
    174159
    175         if (this.chart.Series.Contains(qualitySeries) && qualitySeries.Points.Count() != 0) {
    176           FillSeries(Content.Qualities, Content.Solutions, qualitySeries);
    177         }
    178         if (this.chart.Series.Contains(paretoSeries) && paretoSeries.Points.Count() != 0) {
    179           FillSeries(Content.ParetoFront, null, paretoSeries);
    180         }
     160      int objectives = qualities[0].Length;
    181161
     162      // "Global" dimension index describes the index as if the qualities and solutions would be in a single array
     163      // If the global dimension index is too long for the qualities, use solutions
     164      var xDimArray = xDimGlobal < objectives ? qualities : solutions;
     165      var yDimArray = yDimGlobal < objectives ? qualities : solutions;
     166      var xDimIndex = xDimGlobal < objectives ? xDimGlobal : xDimGlobal - objectives;
     167      var yDimIndex = yDimGlobal < objectives ? yDimGlobal : yDimGlobal - objectives;
    182168
    183         double minX = Double.MaxValue;
    184         double maxX = Double.MinValue;
    185         double minY = Double.MaxValue;
    186         double maxY = Double.MinValue;
    187         foreach (Series s in this.chart.Series) {
    188           if (s.Points.Count == 0) continue;
    189           minX = Math.Min(minX, s.Points.Select(p => p.XValue).Min());
    190           maxX = Math.Max(maxX, s.Points.Select(p => p.XValue).Max());
    191           minY = Math.Min(minY, s.Points.Select(p => p.YValues.Min()).Min());
    192           maxY = Math.Max(maxY, s.Points.Select(p => p.YValues.Max()).Max());
    193         }
     169      if (xDimArray == null || yDimArray == null)
     170        return new Point2D<double>[0];
    194171
    195         maxX = maxX + 0.2 * Math.Abs(maxX);
    196         minX = minX - 0.2 * Math.Abs(minX);
    197         maxY = maxY + 0.2 * Math.Abs(maxY);
    198         minY = minY - 0.2 * Math.Abs(minY);
    199 
    200         double interestingValuesRangeX = maxX - minX;
    201         double interestingValuesRangeY = maxY - minY;
    202 
    203         int digitsX = Math.Max(0, 3 - (int)Math.Log10(interestingValuesRangeX));
    204         int digitsY = Math.Max(0, 3 - (int)Math.Log10(interestingValuesRangeY));
    205 
    206 
    207         maxX = Math.Round(maxX, digitsX);
    208         minX = Math.Round(minX, digitsX);
    209         maxY = Math.Round(maxY, digitsY);
    210         minY = Math.Round(minY, digitsY);
    211         if (minX > maxX) {
    212           minX = 0;
    213           maxX = 1;
    214         }
    215         if (minY > maxY) {
    216           minY = 0;
    217           maxY = 1;
    218         }
    219 
    220 
    221         this.chart.ChartAreas[0].AxisX.Maximum = maxX;
    222         this.chart.ChartAreas[0].AxisX.Minimum = minX;
    223         this.chart.ChartAreas[0].AxisY.Maximum = maxY;
    224         this.chart.ChartAreas[0].AxisY.Minimum = minY;
    225         UpdateCursorInterval();
     172      var points = new Point2D<double>[xDimArray.Length];
     173      for (int i = 0; i < xDimArray.Length; i++) {
     174        points[i] = new Point2D<double>(xDimArray[i][xDimIndex], yDimArray[i][yDimIndex]);
    226175      }
     176      return points;
    227177    }
    228178
    229     private void ClearChart() {
    230       if (chart.Series.Contains(qualitySeries)) chart.Series.Remove(qualitySeries);
    231       if (chart.Series.Contains(paretoSeries)) chart.Series.Remove(paretoSeries);
    232       BuildEmptySeries();
     179    #region Event Handler
     180    private void axisComboBox_SelectedIndexChanged(object sender, EventArgs e) {
     181      if (suppressEvents) return;
     182      UpdateAxisDescription();
     183      UpdateChartData();
    233184    }
    234 
    235     private void ToggleSeriesData(Series series) {
    236       if (series.Points.Count > 0) {  //checks if series is shown
    237         series.Points.Clear();
    238       } else if (Content != null) {
    239         switch (series.Name) {
    240           case PARETO_FRONT:
    241             FillSeries(Content.ParetoFront, null, this.chart.Series[PARETO_FRONT]);
    242             break;
    243           case QUALITIES:
    244             FillSeries(Content.Qualities, Content.Solutions, this.chart.Series[QUALITIES]);
    245             break;
    246         }
    247       }
    248     }
    249 
    250     private void chart_MouseDown(object sender, MouseEventArgs e) {
    251       HitTestResult result = chart.HitTest(e.X, e.Y);
    252       if (result.ChartElementType == ChartElementType.LegendItem) {
    253         this.ToggleSeriesData(result.Series);
    254       }
    255 
    256     }
    257 
    258     private void chart_MouseMove(object sender, MouseEventArgs e) {
    259       HitTestResult result = chart.HitTest(e.X, e.Y);
    260       if (result.ChartElementType == ChartElementType.LegendItem)
    261         this.Cursor = Cursors.Hand;
    262       else
    263         this.Cursor = Cursors.Default;
    264     }
    265 
    266     private void chart_AxisViewChanged(object sender, System.Windows.Forms.DataVisualization.Charting.ViewEventArgs e) {
    267       this.chart.ChartAreas[0].AxisX.ScaleView.Size = e.NewSize;
    268       this.chart.ChartAreas[0].AxisY.ScaleView.Size = e.NewSize;
    269     }
    270 
    271     private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
    272       if (this.chart.Series.Contains(qualitySeries)) e.LegendItems[0].Cells[1].ForeColor = this.chart.Series[QUALITIES].Points.Count == 0 ? Color.Gray : Color.Black;
    273       if (this.chart.Series.Contains(paretoSeries)) e.LegendItems[1].Cells[1].ForeColor = this.chart.Series[PARETO_FRONT].Points.Count == 0 ? Color.Gray : Color.Black;
    274     }
    275 
    276     private void AddMenuItems() {
    277       chooseDimensionToolStripMenuItem.DropDownItems.Clear();
    278       chooseYDimensionToolStripMenuItem.DropDownItems.Clear();
    279       if (Content == null) { return; }
    280       int i = 0;
    281       for (; i < Content.Objectives; i++) {
    282         //add Menu Points
    283         ToolStripMenuItem xItem = MakeMenuItem("X", "Objective " + i, i);
    284         ToolStripMenuItem yItem = MakeMenuItem("Y", "Objective " + i, i);
    285         xItem.Click += new System.EventHandler(this.XMenu_Click);
    286         yItem.Click += new System.EventHandler(this.YMenu_Click);
    287         chooseDimensionToolStripMenuItem.DropDownItems.Add(xItem);
    288         chooseYDimensionToolStripMenuItem.DropDownItems.Add(yItem);
    289       }
    290 
    291       for (; i < Content.Solutions[0].Length + Content.Objectives; i++) {
    292         ToolStripMenuItem xItem = MakeMenuItem("X", "ProblemDimension " + (i - Content.Objectives), i);
    293         ToolStripMenuItem yItem = MakeMenuItem("Y", "ProblemDimension " + (i - Content.Objectives), i); ;
    294         xItem.Click += new System.EventHandler(this.XMenu_Click);
    295         yItem.Click += new System.EventHandler(this.YMenu_Click);
    296         chooseDimensionToolStripMenuItem.DropDownItems.Add(xItem);
    297         chooseYDimensionToolStripMenuItem.DropDownItems.Add(yItem);
    298       }
    299     }
    300 
    301     private ToolStripMenuItem MakeMenuItem(String axis, String label, int i) {
    302       ToolStripMenuItem xItem = new ToolStripMenuItem();
    303       xItem.Name = "obj" + i;
    304       xItem.Size = new System.Drawing.Size(269, 38);
    305       xItem.Text = label;
    306       return xItem;
    307     }
    308 
    309     private void YMenu_Click(object sender, EventArgs e) {
    310       ToolStripMenuItem item = (ToolStripMenuItem)sender;
    311       yDim = Int32.Parse(item.Name.Remove(0, 3));
    312       String label = item.Text;
    313       this.chooseYDimensionToolStripMenuItem.Text = label;
    314       this.chart.ChartAreas[0].AxisY.Title = label;
    315       UpdateChart();
    316     }
    317 
    318     private void XMenu_Click(object sender, EventArgs e) {
    319       ToolStripMenuItem item = (ToolStripMenuItem)sender;
    320       xDim = Int32.Parse(item.Name.Remove(0, 3));
    321       String label = item.Text;
    322       this.chooseDimensionToolStripMenuItem.Text = label;
    323       this.chart.ChartAreas[0].AxisX.Title = label;
    324       UpdateChart();
    325     }
    326 
    327     private void FillSeries(double[][] qualities, double[][] solutions, Series series) {
    328       series.Points.Clear();
    329       if (qualities == null || qualities.Length == 0) return;
    330       int jx = xDim - qualities[0].Length;
    331       int jy = yDim - qualities[0].Length;
    332       if ((jx >= 0 || jy >= 0) && solutions == null) {
    333         return;
    334       }
    335       for (int i = 0; i < qualities.Length; i++) {   //Assumtion: Columnwise
    336         double[] d = qualities[i];
    337         double[] q = null;
    338         if (jx >= 0 || jy >= 0) { q = solutions[i]; }
    339         series.Points.AddXY(jx < 0 ? d[xDim] : q[jx], jy < 0 ? d[yDim] : q[jy]);
    340       }
    341     }
    342 
    343     private void BuildEmptySeries() {
    344 
    345       this.chart.Series.Add(QUALITIES);
    346       qualitySeries = this.chart.Series[QUALITIES];
    347 
    348       this.chart.Series[QUALITIES].LegendText = QUALITIES;
    349       this.chart.Series[QUALITIES].ChartType = SeriesChartType.FastPoint;
    350 
    351       this.chart.Series.Add(PARETO_FRONT);
    352       paretoSeries = this.chart.Series[PARETO_FRONT];
    353       paretoSeries.Color = Color.FromArgb(100, Color.Orange);
    354       this.chart.Series[PARETO_FRONT].LegendText = PARETO_FRONT;
    355       this.chart.Series[PARETO_FRONT].ChartType = SeriesChartType.FastPoint;
    356     }
     185    #endregion
    357186  }
    358187}
    359 
Note: See TracChangeset for help on using the changeset viewer.