source: stable/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 9931

Last change on this file since 9931 was 9931, checked in by ascheibe, 6 years ago

#1730 merged r9587,r9590, r9600, r9607, r9626, r9658, r9659, r9699, r9906 into stable

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