Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 1035 was 996, checked in by bspisic, 16 years ago

#424
RectangleShape -> Opacity property added

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