Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TreeSimplifier/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 8935

Last change on this file since 8935 was 8935, checked in by bburlacu, 12 years ago

#1763: Bugfixes and refactoring as suggested in the comments above.

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