Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 8980

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

#1763: Refactored the tree simplifier. Improved tree/node edit operations.

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