Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Visualization/LineChart.cs @ 981

Last change on this file since 981 was 981, checked in by dwagner, 16 years ago

Added Feature: ZoomToFullView, which shows the whole datarows in the chart. #345

File size: 7.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Drawing;
5using System.Windows.Forms;
6using HeuristicLab.Core;
7
8namespace HeuristicLab.Visualization {
9  public partial class LineChart : ViewBase {
10    private readonly IChartDataRowsModel model;
11    private int maxDataRowCount;
12    private Boolean zoomFullView;
13    private double minDataValue;
14    private double maxDataValue;
15
16    /// <summary>
17    /// This constructor shouldn't be called. Only required for the designer.
18    /// </summary>
19    public LineChart() {
20      InitializeComponent();
21    }
22
23    /// <summary>
24    /// Initializes the chart.
25    /// </summary>
26    /// <param name="model">Referenz to the model, for data</param>
27    public LineChart(IChartDataRowsModel model) : this() {
28      if (model == null) {
29        throw new NullReferenceException("Model cannot be null.");
30      }
31
32      //TODO: correct Rectangle to fit
33      RectangleD clientRectangle = new RectangleD(-1, -1, 1, 1);
34      canvasUI1.MainCanvas.WorldShape = new WorldShape(clientRectangle, clientRectangle);
35         
36      CreateMouseEventListeners();
37         
38      this.model = model;
39      Item = (IItem)model;
40      maxDataRowCount = 0;
41      //The whole data rows are shown per default
42      zoomFullView = true;
43      minDataValue = Double.PositiveInfinity;
44      maxDataValue = Double.NegativeInfinity;
45
46    }
47
48    #region Add-/RemoveItemEvents
49
50    protected override void AddItemEvents() {
51      base.AddItemEvents();
52
53      model.DataRowAdded += OnDataRowAdded;
54      model.DataRowRemoved += OnDataRowRemoved;
55      model.ModelChanged += OnModelChanged;
56
57      foreach (IDataRow row in model.Rows) {
58        OnDataRowAdded(row);
59      }
60    }
61
62    protected override void RemoveItemEvents() {
63      base.RemoveItemEvents();
64
65      model.DataRowAdded -= OnDataRowAdded;
66      model.DataRowRemoved -= OnDataRowRemoved;
67      model.ModelChanged -= OnModelChanged;
68    }
69
70    private void OnDataRowAdded(IDataRow row) {
71      row.ValueChanged += OnRowValueChanged;
72      row.ValuesChanged += OnRowValuesChanged;
73      if (row.Count > maxDataRowCount)
74        maxDataRowCount = row.Count;
75     
76      InitShapes(row);
77    }
78
79    private void ZoomToFullView() {
80      if(!zoomFullView)
81        return;
82      RectangleD newClippingArea =  new RectangleD(-0.1,
83        minDataValue-((maxDataValue-minDataValue)*0.05),
84        maxDataRowCount-0.9,
85        maxDataValue + ((maxDataValue - minDataValue) * 0.05));
86      canvasUI1.MainCanvas.WorldShape.ClippingArea = newClippingArea;
87    }
88
89    private void InitShapes(IDataRow row) {
90     
91       
92      List<LineShape> lineShapes = new List<LineShape>();
93      if (row.Count > 0) {
94        maxDataValue = Max(row[0], this.maxDataValue);
95        minDataValue = Min(row[0], minDataValue);
96      }
97      for (int i = 1; i < row.Count; i++) {
98        LineShape lineShape = new LineShape(i - 1, row[i - 1], i, row[i], 0, row.Color, row.Thickness, row.Style);
99        lineShapes.Add(lineShape);
100        // TODO each DataRow needs its own WorldShape so Y Axes can be zoomed independently.
101        canvasUI1.MainCanvas.WorldShape.AddShape(lineShape);
102        maxDataValue = Max(row[i], maxDataValue);
103        minDataValue = Min(row[i], minDataValue);
104      }
105
106      rowToLineShapes[row] = lineShapes;
107      ZoomToFullView();
108      canvasUI1.Invalidate();
109    }
110
111    private double Min(double d, double value) {
112      return d < value ? d : value;
113    }
114
115    private double Max(double d, double value) {
116      return d>value ? d : value;
117    }
118
119    private void OnDataRowRemoved(IDataRow row) {
120      row.ValueChanged -= OnRowValueChanged;
121      row.ValuesChanged -= OnRowValuesChanged;
122    }
123
124    private readonly IDictionary<IDataRow, List<LineShape>> rowToLineShapes = new Dictionary<IDataRow, List<LineShape>>();
125
126    // TODO use action parameter
127    private void OnRowValueChanged(IDataRow row, double value, int index, Action action) {
128      List<LineShape> lineShapes = rowToLineShapes[row];
129      maxDataValue = Max(value, maxDataValue);
130      minDataValue = Min(value, minDataValue);
131
132      if (index > lineShapes.Count + 1) {
133        throw new NotImplementedException();
134      }
135
136      // new value was added
137      if (index > 0 && index == lineShapes.Count + 1) {
138       
139        if (maxDataRowCount < row.Count)
140          maxDataRowCount = row.Count;
141        LineShape lineShape = new LineShape(index - 1, row[index - 1], index, row[index], 0, row.Color, row.Thickness, row.Style);
142        lineShapes.Add(lineShape);
143        // TODO each DataRow needs its own WorldShape so Y Axes can be zoomed independently.
144        canvasUI1.MainCanvas.WorldShape.AddShape(lineShape);
145      }
146
147      // not the first value
148      if (index > 0) {
149        lineShapes[index - 1].Y2 = value;
150      }
151
152      // not the last value
153      if (index > 0 && index < row.Count - 1) {
154        lineShapes[index].Y1 = value;
155      }
156      ZoomToFullView();
157      canvasUI1.Invalidate();
158    }
159
160    // TODO use action parameter
161    private void OnRowValuesChanged(IDataRow row, double[] values, int index, Action action) {
162      foreach (double value in values) {
163        OnRowValueChanged(row, value, index++, action);
164      }
165    }
166
167    private void OnModelChanged() {}
168
169    #endregion
170
171    #region Begin-/EndUpdate
172
173    private int beginUpdateCount = 0;
174
175    public void BeginUpdate() {
176      beginUpdateCount++;
177    }
178
179    public void EndUpdate() {
180      if (beginUpdateCount == 0) {
181        throw new InvalidOperationException("Too many EndUpdates.");
182      }
183
184      beginUpdateCount--;
185
186      if (beginUpdateCount == 0) {
187        Invalidate();
188      }
189    }
190
191    #endregion
192
193    private MouseEventListener panListener;
194
195    private void CreateMouseEventListeners() {
196      panListener = new MouseEventListener();
197      panListener.OnMouseMove += Pan_OnMouseMove;
198      panListener.OnMouseUp += Pan_OnMouseUp;
199    }
200
201
202    private RectangleD startClippingArea;
203
204    private void canvasUI1_MouseDown(object sender, MouseEventArgs e) {
205      panListener.StartPoint = e.Location;
206      canvasUI1.MouseEventListener = panListener;
207
208      startClippingArea = canvasUI1.MainCanvas.WorldShape.ClippingArea;
209    }
210
211    private void Pan_OnMouseUp(Point startPoint, Point actualPoint) {
212       canvasUI1.MouseEventListener = null;
213    }
214
215    private void Pan_OnMouseMove(Point startPoint, Point actualPoint) {
216      Rectangle viewPort = canvasUI1.ClientRectangle;
217
218      PointD worldStartPoint = Transform.ToWorld(startPoint, viewPort, startClippingArea);
219      PointD worldActualPoint = Transform.ToWorld(actualPoint, viewPort, startClippingArea);
220
221      double xDiff = worldActualPoint.X - worldStartPoint.X;
222      double yDiff = worldActualPoint.Y - worldStartPoint.Y;
223
224      RectangleD newClippingArea = new RectangleD();
225      newClippingArea.X1 = startClippingArea.X1 - xDiff;
226      newClippingArea.X2 = startClippingArea.X2 - xDiff;
227      newClippingArea.Y1 = startClippingArea.Y1 - yDiff;
228      newClippingArea.Y2 = startClippingArea.Y2 - yDiff;
229
230      canvasUI1.MainCanvas.WorldShape.ClippingArea = newClippingArea;
231      panListener.StartPoint = startPoint;
232
233      this.zoomFullView = false; //user wants to pan => no full view
234
235      canvasUI1.Invalidate();
236    }
237  }
238}
Note: See TracBrowser for help on using the repository browser.