Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TreeSimplifierView/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 7411

Last change on this file since 7411 was 7411, checked in by bburlacu, 13 years ago

#1763: Improved ValueChangeDialog and overall behavior. Implemented pruning operation on background thread. TODO: Test, improve. This is a work-in-progress.

File size: 15.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Drawing.Imaging;
26using System.Windows.Forms;
27
28namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
29  public sealed partial class SymbolicExpressionTreeChart : UserControl {
30    private Image image;
31    private StringFormat stringFormat;
32    private Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode> visualTreeNodes;
33    private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection> visualLines;
34    private VisualSymbolicExpressionTreeNode selectedNode;
35
36    public SymbolicExpressionTreeChart() {
37      InitializeComponent();
38      this.image = new Bitmap(Width, Height);
39      this.stringFormat = new StringFormat();
40      this.stringFormat.Alignment = StringAlignment.Center;
41      this.stringFormat.LineAlignment = StringAlignment.Center;
42      this.spacing = 5;
43      this.lineColor = Color.Black;
44      this.backgroundColor = Color.White;
45      this.textFont = new Font("Times New Roman", 8);
46      selectedNode = null;
47    }
48
49    public SymbolicExpressionTreeChart(ISymbolicExpressionTree tree)
50      : this() {
51      this.Tree = tree;
52    }
53
54    private int spacing;
55    public int Spacing {
56      get { return this.spacing; }
57      set {
58        this.spacing = value;
59        this.Repaint();
60      }
61    }
62
63    private Color lineColor;
64    public Color LineColor {
65      get { return this.lineColor; }
66      set {
67        this.lineColor = value;
68        this.Repaint();
69      }
70    }
71
72    private Color backgroundColor;
73    public Color BackgroundColor {
74      get { return this.backgroundColor; }
75      set {
76        this.backgroundColor = value;
77        this.Repaint();
78      }
79    }
80
81    private Font textFont;
82    public Font TextFont {
83      get { return this.textFont; }
84      set {
85        this.textFont = value;
86        this.Repaint();
87      }
88    }
89
90    private ISymbolicExpressionTree tree;
91    public ISymbolicExpressionTree Tree {
92      get { return this.tree; }
93      set {
94        tree = value;
95        visualTreeNodes = new Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode>();
96        visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection>();
97        if (tree != null) {
98          foreach (ISymbolicExpressionTreeNode node in tree.IterateNodesPrefix()) {
99            visualTreeNodes[node] = new VisualSymbolicExpressionTreeNode(node);
100            if (node.Parent != null) visualLines[Tuple.Create(node.Parent, node)] = new VisualSymbolicExpressionTreeNodeConnection();
101          }
102        }
103        Repaint();
104      }
105    }
106
107    private bool suspendRepaint;
108    public bool SuspendRepaint {
109      get { return suspendRepaint; }
110      set { suspendRepaint = value; }
111    }
112
113    protected override void OnPaint(PaintEventArgs e) {
114      e.Graphics.DrawImage(image, 0, 0);
115      base.OnPaint(e);
116    }
117    protected override void OnResize(EventArgs e) {
118      base.OnResize(e);
119      if (this.Width <= 1 || this.Height <= 1)
120        this.image = new Bitmap(1, 1);
121      else
122        this.image = new Bitmap(Width, Height);
123      this.Repaint();
124    }
125
126    public void Repaint() {
127      if (!suspendRepaint) {
128        this.GenerateImage();
129        this.Refresh();
130      }
131    }
132
133    private void GenerateImage() {
134      using (Graphics graphics = Graphics.FromImage(image)) {
135        graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
136        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
137        graphics.Clear(backgroundColor);
138        if (tree != null) {
139          int height = this.Height / tree.Depth;
140          DrawFunctionTree(tree, graphics, 0, 0, this.Width, height);
141        }
142      }
143    }
144
145    public VisualSymbolicExpressionTreeNode GetVisualSymbolicExpressionTreeNode(ISymbolicExpressionTreeNode symbolicExpressionTreeNode) {
146      if (visualTreeNodes.ContainsKey(symbolicExpressionTreeNode))
147        return visualTreeNodes[symbolicExpressionTreeNode];
148      return null;
149    }
150
151    public VisualSymbolicExpressionTreeNodeConnection GetVisualSymbolicExpressionTreeNodeConnection(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
152      if (child.Parent != parent) throw new ArgumentException();
153      var key = Tuple.Create(parent, child);
154      VisualSymbolicExpressionTreeNodeConnection connection = null;
155      visualLines.TryGetValue(key, out connection);
156      return connection;
157    }
158
159    #region events
160    public event EventHandler SymbolicExpressionTreeNodeChanged;
161    private void OnSymbolicExpressionTreeNodeChanged(object sender, EventArgs e) {
162      var changed = SymbolicExpressionTreeNodeChanged;
163      if (changed != null) {
164        changed(sender, e);
165      }
166    }
167
168    public event MouseEventHandler SymbolicExpressionTreeNodeClicked;
169    private void OnSymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) {
170      var clicked = SymbolicExpressionTreeNodeClicked;
171      if (clicked != null)
172        clicked(sender, e);
173    }
174
175    private void SymbolicExpressionTreeChart_MouseClick(object sender, MouseEventArgs e) {
176      VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
177      if (visualTreeNode != null)
178        OnSymbolicExpressionTreeNodeClicked(visualTreeNode, e);
179    }
180
181    public event MouseEventHandler SymbolicExpressionTreeNodeDoubleClicked;
182    private void OnSymbolicExpressionTreeNodeDoubleClicked(object sender, MouseEventArgs e) {
183      var doubleClicked = SymbolicExpressionTreeNodeDoubleClicked;
184      if (doubleClicked != null)
185        doubleClicked(sender, e);
186    }
187
188    private void SymbolicExpressionTreeChart_MouseDoubleClick(object sender, MouseEventArgs e) {
189      VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
190      if (visualTreeNode != null)
191        OnSymbolicExpressionTreeNodeDoubleClicked(visualTreeNode, e);
192    }
193
194    public event ItemDragEventHandler SymbolicExpressionTreeNodeDrag;
195    private void OnSymbolicExpressionTreeNodeDragDrag(object sender, ItemDragEventArgs e) {
196      var dragged = SymbolicExpressionTreeNodeDrag;
197      if (dragged != null)
198        dragged(sender, e);
199    }
200
201    private VisualSymbolicExpressionTreeNode draggedSymbolicExpressionTree;
202    private MouseButtons dragButtons;
203    private void SymbolicExpressionTreeChart_MouseDown(object sender, MouseEventArgs e) {
204      this.dragButtons = e.Button;
205      this.draggedSymbolicExpressionTree = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
206    }
207    private void SymbolicExpressionTreeChart_MouseUp(object sender, MouseEventArgs e) {
208      this.draggedSymbolicExpressionTree = null;
209      this.dragButtons = MouseButtons.None;
210    }
211
212    private void SymbolicExpressionTreeChart_MouseMove(object sender, MouseEventArgs e) {
213      VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
214      if (draggedSymbolicExpressionTree != null &&
215        draggedSymbolicExpressionTree != visualTreeNode) {
216        OnSymbolicExpressionTreeNodeDragDrag(draggedSymbolicExpressionTree, new ItemDragEventArgs(dragButtons, draggedSymbolicExpressionTree));
217        draggedSymbolicExpressionTree = null;
218      } else if (draggedSymbolicExpressionTree == null &&
219        visualTreeNode != null) {
220        string tooltipText = visualTreeNode.ToolTip;
221        if (this.toolTip.GetToolTip(this) != tooltipText)
222          this.toolTip.SetToolTip(this, tooltipText);
223
224      } else if (visualTreeNode == null)
225        this.toolTip.SetToolTip(this, "");
226    }
227
228    public VisualSymbolicExpressionTreeNode FindVisualSymbolicExpressionTreeNodeAt(int x, int y) {
229      foreach (var visualTreeNode in visualTreeNodes.Values) {
230        if (x >= visualTreeNode.X && x <= visualTreeNode.X + visualTreeNode.Width &&
231            y >= visualTreeNode.Y && y <= visualTreeNode.Y + visualTreeNode.Height)
232          return visualTreeNode;
233      }
234      return null;
235    }
236    #endregion
237
238    #region methods for painting the symbolic expression tree
239    private void DrawFunctionTree(ISymbolicExpressionTree tree, Graphics graphics, int x, int y, int width, int height) {
240      DrawFunctionTree(tree.Root, graphics, x, y, width, height, Point.Empty);
241    }
242
243    /// <summary>
244    ///
245    /// </summary>
246    /// <param name="functionTree"> functiontree to draw</param>
247    /// <param name="graphics">graphics object to draw on</param>
248    /// <param name="x">x coordinate of drawing area</param>
249    /// <param name="y">y coordinate of drawing area</param>
250    /// <param name="width">width of drawing area</param>
251    /// <param name="height">height of drawing area</param>
252    private void DrawFunctionTree(ISymbolicExpressionTreeNode node, Graphics graphics, int x, int y, int width, int height, Point connectionPoint) {
253      VisualSymbolicExpressionTreeNode visualTreeNode = visualTreeNodes[node];
254      float center_x = x + width / 2;
255      float center_y = y + height / 2;
256      int actualWidth = width - spacing;
257      int actualHeight = height - spacing;
258
259      SolidBrush textBrush = new SolidBrush(visualTreeNode.TextColor);
260      Pen nodeLinePen = new Pen(visualTreeNode.LineColor);
261      SolidBrush nodeFillBrush = new SolidBrush(visualTreeNode.FillColor);
262
263      //calculate size of node
264      if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) {
265        visualTreeNode.Width = visualTreeNode.PreferredWidth;
266        visualTreeNode.Height = visualTreeNode.PreferredHeight;
267        visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2;
268        visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2;
269      }
270        //width too small to draw in desired sized
271      else if (actualWidth < visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) {
272        visualTreeNode.Width = actualWidth;
273        visualTreeNode.Height = visualTreeNode.PreferredHeight;
274        visualTreeNode.X = x;
275        visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2;
276      }
277        //height too small to draw in desired sized
278      else if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight < visualTreeNode.PreferredHeight) {
279        visualTreeNode.Width = visualTreeNode.PreferredWidth;
280        visualTreeNode.Height = actualHeight;
281        visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2;
282        visualTreeNode.Y = y;
283      }
284        //width and height too small to draw in desired size
285      else {
286        visualTreeNode.Width = actualWidth;
287        visualTreeNode.Height = actualHeight;
288        visualTreeNode.X = x;
289        visualTreeNode.Y = y;
290      }
291
292      //draw terminal node
293      if (node.SubtreeCount == 0) {
294        graphics.FillRectangle(nodeFillBrush, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
295        graphics.DrawRectangle(nodeLinePen, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
296      } else {
297        graphics.FillEllipse(nodeFillBrush, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
298        graphics.DrawEllipse(nodeLinePen, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
299      }
300
301      //draw name of symbol
302      var text = node.ToString();
303      graphics.DrawString(text, textFont, textBrush, new RectangleF(visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height), stringFormat);
304
305      //draw connection line to parent node
306      if (!connectionPoint.IsEmpty && node.Parent != null) {
307        var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node.Parent, node);
308        using (Pen linePen = new Pen(visualLine.LineColor)) {
309          linePen.DashStyle = visualLine.DashStyle;
310          graphics.DrawLine(linePen, connectionPoint, new Point(visualTreeNode.X + visualTreeNode.Width / 2, visualTreeNode.Y));
311        }
312      }
313
314      //calculate areas for the subtrees according to their tree size and call drawFunctionTree
315      Point connectFrom = new Point(visualTreeNode.X + visualTreeNode.Width / 2, visualTreeNode.Y + visualTreeNode.Height);
316      int[] xBoundaries = new int[node.SubtreeCount + 1];
317      xBoundaries[0] = x;
318      for (int i = 0; i < node.SubtreeCount; i++) {
319        xBoundaries[i + 1] = (int)(xBoundaries[i] + (width * (double)node.GetSubtree(i).GetLength()) / (node.GetLength() - 1));
320        DrawFunctionTree(node.GetSubtree(i), graphics, xBoundaries[i], y + height,
321          xBoundaries[i + 1] - xBoundaries[i], height, connectFrom);
322      }
323    }
324    #endregion
325
326    #region save image
327    private void saveImageToolStripMenuItem_Click(object sender, EventArgs e) {
328      if (saveFileDialog.ShowDialog() == DialogResult.OK) {
329        string filename = saveFileDialog.FileName.ToLower();
330        if (filename.EndsWith("bmp")) SaveImageAsBitmap(filename);
331        else if (filename.EndsWith("emf")) SaveImageAsEmf(filename);
332        else SaveImageAsBitmap(filename);
333      }
334    }
335
336    private void SaveImageAsBitmap(string filename) {
337      if (tree == null) return;
338      Image image = new Bitmap(Width, Height);
339      using (Graphics g = Graphics.FromImage(image)) {
340        int height = this.Height / tree.Depth;
341        DrawFunctionTree(tree, g, 0, 0, Width, height);
342      }
343      image.Save(filename);
344    }
345
346    private void SaveImageAsEmf(string filename) {
347      if (tree == null) return;
348      using (Graphics g = CreateGraphics()) {
349        using (Metafile file = new Metafile(filename, g.GetHdc())) {
350          using (Graphics emfFile = Graphics.FromImage(file)) {
351            int height = this.Height / tree.Depth;
352            DrawFunctionTree(tree, emfFile, 0, 0, Width, height);
353          }
354        }
355        g.ReleaseHdc();
356      }
357    }
358    #endregion
359
360    private void contextMenuStrip_Opened(object sender, EventArgs e) {
361      var menu = sender as ContextMenuStrip;
362      if (menu == null) return;
363      var point = menu.SourceControl.PointToClient(Cursor.Position);
364      selectedNode = FindVisualSymbolicExpressionTreeNodeAt(point.X, point.Y);
365      if (selectedNode != null) {
366        OnSymbolicExpressionTreeNodeClicked(selectedNode, new MouseEventArgs(MouseButtons.Right, 1, point.X, point.Y, 0));
367      }
368    }
369
370    private void changeValueToolStripMenuItem_Click(object sender, EventArgs e) {
371      if (selectedNode != null) // this should never be null anyway
372        OnSymbolicExpressionTreeNodeChanged(selectedNode, e);
373    }
374  }
375}
Note: See TracBrowser for help on using the repository browser.