Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 985 was 985, checked in by bspisic, 15 years ago

#424
Implemented ResetView

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