- Timestamp:
- 12/02/16 13:06:49 (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/sources/HeuristicLab.Analysis.Views/3.3/ScatterPlotView.cs
r14438 r14439 21 21 22 22 using System; 23 using System.Collections.Generic;24 using System.Drawing;25 using System.Linq;26 23 using System.Windows.Forms; 27 using System.Windows.Forms.DataVisualization.Charting;28 using HeuristicLab.Collections;29 using HeuristicLab.Common;30 24 using HeuristicLab.Core.Views; 31 25 using HeuristicLab.MainForm; … … 35 29 [Content(typeof(ScatterPlot), true)] 36 30 public partial class ScatterPlotView : NamedItemView, IConfigureableView { 37 protected List<Series> invisibleSeries;38 protected Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow> pointsRowsTable;39 private double xMin, xMax, yMin, yMax;40 31 41 32 public new ScatterPlot Content { … … 46 37 public ScatterPlotView() { 47 38 InitializeComponent(); 48 pointsRowsTable = new Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow>();49 invisibleSeries = new List<Series>();50 chart.CustomizeAllChartAreas();51 chart.ChartAreas[0].CursorX.Interval = 1;52 chart.ContextMenuStrip.Items.Add(configureToolStripMenuItem);53 39 } 54 55 #region Event Handler Registration56 protected override void DeregisterContentEvents() {57 foreach (ScatterPlotDataRow row in Content.Rows)58 DeregisterScatterPlotDataRowEvents(row);59 Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);60 Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsAdded);61 Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsRemoved);62 Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsReplaced);63 Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_CollectionReset);64 base.DeregisterContentEvents();65 }66 protected override void RegisterContentEvents() {67 base.RegisterContentEvents();68 Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);69 Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsAdded);70 Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsRemoved);71 Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsReplaced);72 Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_CollectionReset);73 }74 75 protected virtual void RegisterScatterPlotDataRowEvents(ScatterPlotDataRow row) {76 row.NameChanged += new EventHandler(Row_NameChanged);77 row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);78 pointsRowsTable.Add(row.Points, row);79 row.Points.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded);80 row.Points.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved);81 row.Points.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced);82 row.Points.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset);83 }84 protected virtual void DeregisterScatterPlotDataRowEvents(ScatterPlotDataRow row) {85 row.Points.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded);86 row.Points.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved);87 row.Points.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced);88 row.Points.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset);89 pointsRowsTable.Remove(row.Points);90 row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);91 row.NameChanged -= new EventHandler(Row_NameChanged);92 }93 #endregion94 40 95 41 protected override void OnContentChanged() { 96 42 base.OnContentChanged(); 97 invisibleSeries.Clear(); 98 chart.Titles[0].Text = string.Empty; 99 chart.ChartAreas[0].AxisX.Title = string.Empty; 100 chart.ChartAreas[0].AxisY.Title = string.Empty; 101 chart.Series.Clear(); 102 if (Content != null) { 103 chart.Titles[0].Text = Content.Name; 104 chart.Titles[0].Visible = !string.IsNullOrEmpty(Content.Name); 105 AddScatterPlotDataRows(Content.Rows); 106 ConfigureChartArea(chart.ChartAreas[0]); 107 RecalculateMinMaxPointValues(); 108 RecalculateAxesScale(chart.ChartAreas[0]); 109 } 43 chart.Content = Content; 110 44 } 111 45 … … 116 50 117 51 public void ShowConfiguration() { 118 if (Content != null) { 119 using (ScatterPlotVisualPropertiesDialog dialog = new ScatterPlotVisualPropertiesDialog(Content)) { 120 dialog.ShowDialog(this); 121 } 122 } else MessageBox.Show("Nothing to configure."); 52 chart.ShowConfiguration(); 123 53 } 124 54 125 protected virtual void AddScatterPlotDataRows(IEnumerable<ScatterPlotDataRow> rows) {126 foreach (var row in rows) {127 RegisterScatterPlotDataRowEvents(row);128 Series series = new Series(row.Name);129 if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;130 else series.LegendText = row.Name;131 ConfigureSeries(series, row);132 FillSeriesWithRowValues(series, row);133 chart.Series.Add(series);134 }135 ConfigureChartArea(chart.ChartAreas[0]);136 RecalculateMinMaxPointValues();137 RecalculateAxesScale(chart.ChartAreas[0]);138 UpdateYCursorInterval();139 }140 141 protected virtual void RemoveScatterPlotDataRows(IEnumerable<ScatterPlotDataRow> rows) {142 foreach (var row in rows) {143 DeregisterScatterPlotDataRowEvents(row);144 Series series = chart.Series[row.Name];145 chart.Series.Remove(series);146 if (invisibleSeries.Contains(series))147 invisibleSeries.Remove(series);148 }149 RecalculateMinMaxPointValues();150 RecalculateAxesScale(chart.ChartAreas[0]);151 }152 153 private void ConfigureSeries(Series series, ScatterPlotDataRow row) {154 series.BorderWidth = 1;155 series.BorderDashStyle = ChartDashStyle.Solid;156 series.BorderColor = Color.Empty;157 158 if (row.VisualProperties.Color != Color.Empty)159 series.Color = row.VisualProperties.Color;160 else series.Color = Color.Empty;161 series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;162 series.ChartType = SeriesChartType.FastPoint;163 series.MarkerSize = row.VisualProperties.PointSize;164 series.MarkerStyle = ConvertPointStyle(row.VisualProperties.PointStyle);165 series.XAxisType = AxisType.Primary;166 series.YAxisType = AxisType.Primary;167 168 if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;169 else series.LegendText = row.Name;170 171 string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)172 ? "X"173 : Content.VisualProperties.XAxisTitle;174 string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)175 ? "Y"176 : Content.VisualProperties.YAxisTitle;177 series.ToolTip =178 series.LegendText + Environment.NewLine +179 xAxisTitle + " = " + "#VALX," + Environment.NewLine +180 yAxisTitle + " = " + "#VAL";181 }182 183 private void ConfigureChartArea(ChartArea area) {184 if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;185 if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;186 chart.Titles[0].Text = Content.VisualProperties.Title;187 chart.Titles[0].Visible = !string.IsNullOrEmpty(Content.VisualProperties.Title);188 189 if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;190 if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;191 area.AxisX.Title = Content.VisualProperties.XAxisTitle;192 area.AxisX.MajorGrid.Enabled = Content.VisualProperties.XAxisGrid;193 194 if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;195 if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;196 area.AxisY.Title = Content.VisualProperties.YAxisTitle;197 area.AxisY.MajorGrid.Enabled = Content.VisualProperties.YAxisGrid;198 }199 200 private void RecalculateAxesScale(ChartArea area) {201 area.AxisX.Minimum = CalculateMinBound(xMin);202 area.AxisX.Maximum = CalculateMaxBound(xMax);203 if (area.AxisX.Minimum == area.AxisX.Maximum) {204 area.AxisX.Minimum = xMin - 0.5;205 area.AxisX.Maximum = xMax + 0.5;206 }207 area.AxisY.Minimum = CalculateMinBound(yMin);208 area.AxisY.Maximum = CalculateMaxBound(yMax);209 if (area.AxisY.Minimum == area.AxisY.Maximum) {210 area.AxisY.Minimum = yMin - 0.5;211 area.AxisY.Maximum = yMax + 0.5;212 }213 if (xMax - xMin > 0) area.CursorX.Interval = Math.Pow(10, Math.Floor(Math.Log10(area.AxisX.Maximum - area.AxisX.Minimum) - 3));214 else area.CursorX.Interval = 1;215 area.AxisX.IsMarginVisible = false;216 217 if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;218 if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;219 if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;220 if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;221 }222 223 private static double CalculateMinBound(double min) {224 if (min == 0) return 0;225 var scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(min))));226 return scale * (Math.Floor(min / scale));227 }228 229 private static double CalculateMaxBound(double max) {230 if (max == 0) return 0;231 var scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(max))));232 return scale * (Math.Ceiling(max / scale));233 }234 235 protected virtual void UpdateYCursorInterval() {236 double interestingValuesRange = (237 from series in chart.Series238 where series.Enabled239 let values = (from point in series.Points240 where !point.IsEmpty241 select point.YValues[0]).DefaultIfEmpty(1.0)242 let range = values.Max() - values.Min()243 where range > 0.0244 select range245 ).DefaultIfEmpty(1.0).Min();246 247 double digits = (int)Math.Log10(interestingValuesRange) - 3;248 double yZoomInterval = Math.Pow(10, digits);249 this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;250 }251 252 #region Event Handlers253 55 #region Content Event Handlers 254 56 protected override void Content_NameChanged(object sender, EventArgs e) { … … 256 58 Invoke(new EventHandler(Content_NameChanged), sender, e); 257 59 else { 258 chart.Titles[0].Text = Content.Name; 259 chart.Titles[0].Visible = !string.IsNullOrEmpty(Content.Name); 60 Content.VisualProperties.Title = Content.Name; 260 61 base.Content_NameChanged(sender, e); 261 62 } 262 }263 private void Content_VisualPropertiesChanged(object sender, EventArgs e) {264 if (InvokeRequired)265 Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);266 else {267 ConfigureChartArea(chart.ChartAreas[0]);268 RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed269 }270 }271 #endregion272 #region Rows Event Handlers273 private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {274 if (InvokeRequired)275 Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsAdded), sender, e);276 else {277 AddScatterPlotDataRows(e.Items);278 }279 }280 private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {281 if (InvokeRequired)282 Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsRemoved), sender, e);283 else {284 RemoveScatterPlotDataRows(e.Items);285 }286 }287 private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {288 if (InvokeRequired)289 Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsReplaced), sender, e);290 else {291 RemoveScatterPlotDataRows(e.OldItems);292 AddScatterPlotDataRows(e.Items);293 }294 }295 private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {296 if (InvokeRequired)297 Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_CollectionReset), sender, e);298 else {299 RemoveScatterPlotDataRows(e.OldItems);300 AddScatterPlotDataRows(e.Items);301 }302 }303 #endregion304 #region Row Event Handlers305 private void Row_VisualPropertiesChanged(object sender, EventArgs e) {306 if (InvokeRequired)307 Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);308 else {309 ScatterPlotDataRow row = (ScatterPlotDataRow)sender;310 Series series = chart.Series[row.Name];311 series.Points.Clear();312 ConfigureSeries(series, row);313 FillSeriesWithRowValues(series, row);314 RecalculateMinMaxPointValues();315 RecalculateAxesScale(chart.ChartAreas[0]);316 }317 }318 private void Row_NameChanged(object sender, EventArgs e) {319 if (InvokeRequired)320 Invoke(new EventHandler(Row_NameChanged), sender, e);321 else {322 ScatterPlotDataRow row = (ScatterPlotDataRow)sender;323 chart.Series[row.Name].Name = row.Name;324 }325 }326 #endregion327 #region Points Event Handlers328 private void Points_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {329 if (InvokeRequired)330 Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded), sender, e);331 else {332 ScatterPlotDataRow row = null;333 pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);334 if (row != null) {335 Series rowSeries = chart.Series[row.Name];336 if (!invisibleSeries.Contains(rowSeries)) {337 rowSeries.Points.Clear();338 FillSeriesWithRowValues(rowSeries, row);339 RecalculateMinMaxPointValues();340 RecalculateAxesScale(chart.ChartAreas[0]);341 UpdateYCursorInterval();342 }343 }344 }345 }346 private void Points_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {347 if (InvokeRequired)348 Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved), sender, e);349 else {350 ScatterPlotDataRow row = null;351 pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);352 if (row != null) {353 Series rowSeries = chart.Series[row.Name];354 if (!invisibleSeries.Contains(rowSeries)) {355 rowSeries.Points.Clear();356 FillSeriesWithRowValues(rowSeries, row);357 RecalculateMinMaxPointValues();358 RecalculateAxesScale(chart.ChartAreas[0]);359 UpdateYCursorInterval();360 }361 }362 }363 }364 private void Points_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {365 if (InvokeRequired)366 Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced), sender, e);367 else {368 ScatterPlotDataRow row = null;369 pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);370 if (row != null) {371 Series rowSeries = chart.Series[row.Name];372 if (!invisibleSeries.Contains(rowSeries)) {373 rowSeries.Points.Clear();374 FillSeriesWithRowValues(rowSeries, row);375 RecalculateMinMaxPointValues();376 RecalculateAxesScale(chart.ChartAreas[0]);377 UpdateYCursorInterval();378 }379 }380 }381 }382 private void Points_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {383 if (InvokeRequired)384 Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset), sender, e);385 else {386 ScatterPlotDataRow row = null;387 pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);388 if (row != null) {389 Series rowSeries = chart.Series[row.Name];390 if (!invisibleSeries.Contains(rowSeries)) {391 rowSeries.Points.Clear();392 FillSeriesWithRowValues(rowSeries, row);393 RecalculateMinMaxPointValues();394 RecalculateAxesScale(chart.ChartAreas[0]);395 UpdateYCursorInterval();396 }397 }398 }399 }400 #endregion401 private void configureToolStripMenuItem_Click(object sender, System.EventArgs e) {402 ShowConfiguration();403 }404 #endregion405 406 #region Chart Event Handlers407 private void chart_MouseDown(object sender, MouseEventArgs e) {408 HitTestResult result = chart.HitTest(e.X, e.Y);409 if (result.ChartElementType == ChartElementType.LegendItem) {410 ToggleSeriesVisible(result.Series);411 }412 }413 private void chart_MouseMove(object sender, MouseEventArgs e) {414 HitTestResult result = chart.HitTest(e.X, e.Y);415 if (result.ChartElementType == ChartElementType.LegendItem)416 this.Cursor = Cursors.Hand;417 else418 this.Cursor = Cursors.Default;419 }420 private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {421 foreach (LegendItem legendItem in e.LegendItems) {422 var series = chart.Series[legendItem.SeriesName];423 if (series != null) {424 bool seriesIsInvisible = invisibleSeries.Contains(series);425 foreach (LegendCell cell in legendItem.Cells) {426 cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;427 }428 }429 }430 }431 #endregion432 433 private void ToggleSeriesVisible(Series series) {434 if (!invisibleSeries.Contains(series)) {435 series.Points.Clear();436 invisibleSeries.Add(series);437 RecalculateMinMaxPointValues();438 } else {439 invisibleSeries.Remove(series);440 if (Content != null) {441 442 var row = (from r in Content.Rows443 where r.Name == series.Name444 select r).Single();445 FillSeriesWithRowValues(series, row);446 RecalculateMinMaxPointValues();447 this.chart.Legends[series.Legend].ForeColor = Color.Black;448 RecalculateAxesScale(chart.ChartAreas[0]);449 UpdateYCursorInterval();450 }451 }452 }453 454 private void RecalculateMinMaxPointValues() {455 yMin = xMin = double.MaxValue;456 yMax = xMax = double.MinValue;457 foreach (var s in chart.Series.Where(x => x.Enabled)) {458 foreach (var p in s.Points) {459 double x = p.XValue, y = p.YValues[0];460 if (xMin > x) xMin = x;461 if (xMax < x) xMax = x;462 if (yMin > y) yMin = y;463 if (yMax < y) yMax = y;464 }465 }466 }467 468 private void FillSeriesWithRowValues(Series series, ScatterPlotDataRow row) {469 for (int i = 0; i < row.Points.Count; i++) {470 var value = row.Points[i];471 DataPoint point = new DataPoint();472 if (IsInvalidValue(value.X) || IsInvalidValue(value.Y))473 point.IsEmpty = true;474 else {475 point.XValue = value.X;476 point.YValues = new double[] { value.Y };477 }478 series.Points.Add(point);479 }480 }481 482 #region Helpers483 private MarkerStyle ConvertPointStyle(ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle pointStyle) {484 switch (pointStyle) {485 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Circle:486 return MarkerStyle.Circle;487 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Cross:488 return MarkerStyle.Cross;489 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Diamond:490 return MarkerStyle.Diamond;491 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Square:492 return MarkerStyle.Square;493 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star4:494 return MarkerStyle.Star4;495 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star5:496 return MarkerStyle.Star5;497 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star6:498 return MarkerStyle.Star6;499 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star10:500 return MarkerStyle.Star10;501 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Triangle:502 return MarkerStyle.Triangle;503 default:504 return MarkerStyle.None;505 }506 }507 508 protected static bool IsInvalidValue(double x) {509 return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;510 63 } 511 64 #endregion
Note: See TracChangeset
for help on using the changeset viewer.