Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionTracking/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 10586

Last change on this file since 10586 was 10524, checked in by bburlacu, 11 years ago

#1772: Reverse-merged erroneous commit (booked on ticked #2076), and merged latest trunk changes.

File size: 16.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 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.IO;
27using System.Linq;
28using System.Windows.Forms;
29using Point = System.Drawing.Point;
30
31namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
32  public partial class SymbolicExpressionTreeChart : UserControl {
33    private Image image;
34    private readonly StringFormat stringFormat;
35    private Dictionary<ISymbolicExpressionTreeNode, VisualTreeNode<ISymbolicExpressionTreeNode>> visualTreeNodes;
36    private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualTreeNodeConnection> visualLines;
37    private ILayoutEngine<ISymbolicExpressionTreeNode> layoutEngine;
38
39    private const int preferredNodeWidth = 70;
40    private const int preferredNodeHeight = 46;
41    private const int minHorizontalDistance = 30;
42    private const int minVerticalDistance = 30;
43
44    public SymbolicExpressionTreeChart() {
45      InitializeComponent();
46      this.image = new Bitmap(Width, Height);
47      this.stringFormat = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
48      this.spacing = 5;
49      this.lineColor = Color.Black;
50      this.backgroundColor = Color.White;
51      this.textFont = new Font(FontFamily.GenericSansSerif, 12);
52      //      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> {
53      layoutEngine = new BoxesLayoutEngine<ISymbolicExpressionTreeNode> {
54        NodeWidth = preferredNodeWidth,
55        NodeHeight = preferredNodeHeight,
56        HorizontalSpacing = minHorizontalDistance,
57        VerticalSpacing = minVerticalDistance
58      };
59    }
60
61    public SymbolicExpressionTreeChart(ISymbolicExpressionTree tree)
62      : this() {
63      this.Tree = tree;
64    }
65
66    #region Public properties
67    private int spacing;
68    public int Spacing {
69      get { return this.spacing; }
70      set {
71        this.spacing = value;
72        this.Repaint();
73      }
74    }
75
76    private Color lineColor;
77    public Color LineColor {
78      get { return this.lineColor; }
79      set {
80        this.lineColor = value;
81        this.Repaint();
82      }
83    }
84
85    private Color backgroundColor;
86    public Color BackgroundColor {
87      get { return this.backgroundColor; }
88      set {
89        this.backgroundColor = value;
90        this.Repaint();
91      }
92    }
93
94    private Font textFont;
95    public Font TextFont {
96      get { return this.textFont; }
97      set {
98        this.textFont = value;
99        this.Repaint();
100      }
101    }
102
103    private ISymbolicExpressionTree tree;
104    public ISymbolicExpressionTree Tree {
105      get { return this.tree; }
106      set {
107        tree = value;
108        if (tree != null) {
109          Repaint();
110        }
111      }
112    }
113
114    private bool suspendRepaint;
115    public bool SuspendRepaint {
116      get { return suspendRepaint; }
117      set { suspendRepaint = value; }
118    }
119    #endregion
120
121    protected override void OnPaint(PaintEventArgs e) {
122      e.Graphics.DrawImage(image, 0, 0);
123      base.OnPaint(e);
124    }
125    protected override void OnResize(EventArgs e) {
126      base.OnResize(e);
127      if (this.Width <= 1 || this.Height <= 1)
128        this.image = new Bitmap(1, 1);
129      else
130        this.image = new Bitmap(Width, Height);
131      this.Repaint();
132    }
133
134    public void Repaint() {
135      if (!suspendRepaint) {
136        this.GenerateImage();
137        this.Refresh();
138      }
139    }
140
141    public void RepaintNodes() {
142      if (!suspendRepaint) {
143        using (var graphics = Graphics.FromImage(image)) {
144          graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
145          graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
146          foreach (var visualNode in visualTreeNodes.Values) {
147            DrawTreeNode(graphics, visualNode);
148          }
149        }
150        this.Refresh();
151      }
152    }
153
154    public void RepaintNode(VisualTreeNode<ISymbolicExpressionTreeNode> visualNode) {
155      if (!suspendRepaint) {
156        using (var graphics = Graphics.FromImage(image)) {
157          graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
158          graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
159          DrawTreeNode(graphics, visualNode);
160        }
161        this.Refresh();
162      }
163    }
164
165    private void GenerateImage() {
166      using (Graphics graphics = Graphics.FromImage(image)) {
167        graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
168        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
169        graphics.Clear(backgroundColor);
170        if (tree != null) {
171          DrawFunctionTree(tree, graphics, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
172        }
173      }
174    }
175
176    public VisualTreeNode<ISymbolicExpressionTreeNode> GetVisualSymbolicExpressionTreeNode(ISymbolicExpressionTreeNode symbolicExpressionTreeNode) {
177      if (visualTreeNodes.ContainsKey(symbolicExpressionTreeNode))
178        return visualTreeNodes[symbolicExpressionTreeNode];
179      return null;
180    }
181
182    public VisualTreeNodeConnection GetVisualSymbolicExpressionTreeNodeConnection(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
183      if (child.Parent != parent) throw new ArgumentException();
184      var key = Tuple.Create(parent, child);
185      VisualTreeNodeConnection connection = null;
186      visualLines.TryGetValue(key, out connection);
187      return connection;
188    }
189
190    #region events
191    public event MouseEventHandler SymbolicExpressionTreeNodeClicked;
192    protected virtual void OnSymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) {
193      var clicked = SymbolicExpressionTreeNodeClicked;
194      if (clicked != null)
195        clicked(sender, e);
196    }
197
198    protected virtual void SymbolicExpressionTreeChart_MouseClick(object sender, MouseEventArgs e) {
199      var visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
200      if (visualTreeNode != null) {
201        OnSymbolicExpressionTreeNodeClicked(visualTreeNode, e);
202      }
203    }
204
205    public event MouseEventHandler SymbolicExpressionTreeNodeDoubleClicked;
206    protected virtual void OnSymbolicExpressionTreeNodeDoubleClicked(object sender, MouseEventArgs e) {
207      var doubleClicked = SymbolicExpressionTreeNodeDoubleClicked;
208      if (doubleClicked != null)
209        doubleClicked(sender, e);
210    }
211
212    protected virtual void SymbolicExpressionTreeChart_MouseDoubleClick(object sender, MouseEventArgs e) {
213      VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
214      if (visualTreeNode != null)
215        OnSymbolicExpressionTreeNodeDoubleClicked(visualTreeNode, e);
216    }
217
218    public event ItemDragEventHandler SymbolicExpressionTreeNodeDrag;
219    protected virtual void OnSymbolicExpressionTreeNodeDragDrag(object sender, ItemDragEventArgs e) {
220      var dragged = SymbolicExpressionTreeNodeDrag;
221      if (dragged != null)
222        dragged(sender, e);
223    }
224
225    private VisualTreeNode<ISymbolicExpressionTreeNode> draggedSymbolicExpressionTree;
226    private MouseButtons dragButtons;
227    private void SymbolicExpressionTreeChart_MouseDown(object sender, MouseEventArgs e) {
228      this.dragButtons = e.Button;
229      this.draggedSymbolicExpressionTree = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
230    }
231    private void SymbolicExpressionTreeChart_MouseUp(object sender, MouseEventArgs e) {
232      this.draggedSymbolicExpressionTree = null;
233      this.dragButtons = MouseButtons.None;
234    }
235
236    private void SymbolicExpressionTreeChart_MouseMove(object sender, MouseEventArgs e) {
237      VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
238      if (draggedSymbolicExpressionTree != null &&
239        draggedSymbolicExpressionTree != visualTreeNode) {
240        OnSymbolicExpressionTreeNodeDragDrag(draggedSymbolicExpressionTree, new ItemDragEventArgs(dragButtons, draggedSymbolicExpressionTree));
241        draggedSymbolicExpressionTree = null;
242      } else if (draggedSymbolicExpressionTree == null &&
243        visualTreeNode != null) {
244        string tooltipText = visualTreeNode.ToolTip;
245        if (this.toolTip.GetToolTip(this) != tooltipText)
246          this.toolTip.SetToolTip(this, tooltipText);
247
248      } else if (visualTreeNode == null)
249        this.toolTip.SetToolTip(this, "");
250    }
251
252    public VisualTreeNode<ISymbolicExpressionTreeNode> FindVisualSymbolicExpressionTreeNodeAt(int x, int y) {
253      foreach (var visualTreeNode in visualTreeNodes.Values) {
254        if (x >= visualTreeNode.X && x <= visualTreeNode.X + visualTreeNode.Width &&
255            y >= visualTreeNode.Y && y <= visualTreeNode.Y + visualTreeNode.Height)
256          return visualTreeNode;
257      }
258      return null;
259    }
260    #endregion
261
262    #region methods for painting the symbolic expression tree
263    private void DrawFunctionTree(ISymbolicExpressionTree symbExprTree, Graphics graphics, int preferredWidth, int preferredHeight, int minHDistance, int minVDistance) {
264      var root = symbExprTree.Root;
265      var actualRoot = root.SubtreeCount == 1 ? root.GetSubtree(0) : root;
266      layoutEngine.NodeWidth = preferredWidth;
267      layoutEngine.NodeHeight = preferredHeight;
268      layoutEngine.HorizontalSpacing = minHDistance;
269      layoutEngine.VerticalSpacing = minVDistance;
270      layoutEngine.Initialize(actualRoot, n => n.Subtrees, n => n.GetLength(), n => n.GetDepth());
271      layoutEngine.CalculateLayout(Width, Height);
272
273      var visualNodes = layoutEngine.GetVisualNodes().ToList();
274      visualTreeNodes = visualNodes.ToDictionary(x => x.Content, x => x);
275      visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualTreeNodeConnection>();
276      foreach (var node in visualNodes.Select(n => n.Content)) {
277        foreach (var subtree in node.Subtrees) {
278          visualLines.Add(new Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>(node, subtree), new VisualTreeNodeConnection());
279        }
280      }
281      // draw nodes and connections
282      foreach (var visualNode in visualNodes) {
283        DrawTreeNode(visualNode);
284        var node = visualNode.Content;
285        foreach (var subtree in node.Subtrees) {
286          var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node, subtree);
287          var visualSubtree = visualTreeNodes[subtree];
288          var origin = new Point(visualNode.X + visualNode.Width / 2, visualNode.Y + visualNode.Height);
289          var target = new Point(visualSubtree.X + visualSubtree.Width / 2, visualSubtree.Y);
290          graphics.Clip = new Region(new Rectangle(Math.Min(origin.X, target.X), origin.Y, Math.Max(origin.X, target.X), target.Y));
291          using (var linePen = new Pen(visualLine.LineColor)) {
292            linePen.DashStyle = visualLine.DashStyle;
293            graphics.DrawLine(linePen, origin, target);
294          }
295        }
296      }
297    }
298
299    protected void DrawTreeNode(VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode) {
300      using (var graphics = Graphics.FromImage(image)) {
301        graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
302        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
303        DrawTreeNode(graphics, visualTreeNode);
304      }
305    }
306
307    protected void DrawTreeNode(Graphics graphics, VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode) {
308      graphics.Clip = new Region(new Rectangle(visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width + 1, visualTreeNode.Height + 1));
309      graphics.Clear(backgroundColor);
310      var node = visualTreeNode.Content;
311      using (var textBrush = new SolidBrush(visualTreeNode.TextColor))
312      using (var nodeLinePen = new Pen(visualTreeNode.LineColor))
313      using (var nodeFillBrush = new SolidBrush(visualTreeNode.FillColor)) {
314        //draw terminal node
315        if (node.SubtreeCount == 0) {
316          graphics.FillRectangle(nodeFillBrush, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
317          graphics.DrawRectangle(nodeLinePen, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
318        } else {
319          graphics.FillEllipse(nodeFillBrush, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
320          graphics.DrawEllipse(nodeLinePen, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
321        }
322        //draw name of symbol
323        var text = node.ToString();
324        graphics.DrawString(text, textFont, textBrush, new RectangleF(visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height), stringFormat);
325      }
326    }
327    #endregion
328    #region save image
329    private void saveImageToolStripMenuItem_Click(object sender, EventArgs e) {
330      if (saveFileDialog.ShowDialog() == DialogResult.OK) {
331        string filename = saveFileDialog.FileName.ToLower();
332        if (filename.EndsWith("bmp")) SaveImageAsBitmap(filename);
333        else if (filename.EndsWith("emf")) SaveImageAsEmf(filename);
334        else SaveImageAsBitmap(filename);
335      }
336    }
337
338    public void SaveImageAsBitmap(string filename) {
339      if (tree == null) return;
340      Image image = new Bitmap(Width, Height);
341      using (Graphics g = Graphics.FromImage(image)) {
342        DrawFunctionTree(tree, g, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
343      }
344      image.Save(filename);
345    }
346
347    public void SaveImageAsEmf(string filename) {
348      if (tree == null) return;
349      using (Graphics g = CreateGraphics()) {
350        using (Metafile file = new Metafile(filename, g.GetHdc())) {
351          using (Graphics emfFile = Graphics.FromImage(file)) {
352            DrawFunctionTree(tree, emfFile, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
353          }
354        }
355        g.ReleaseHdc();
356      }
357    }
358    #endregion
359    #region export pgf/tikz
360    private void exportLatexToolStripMenuItem_Click(object sender, EventArgs e) {
361      using (var dialog = new SaveFileDialog { Filter = "Tex (*.tex)|*.tex" }) {
362        if (dialog.ShowDialog() != DialogResult.OK) return;
363        string filename = dialog.FileName.ToLower();
364        var formatter = new SymbolicExpressionTreeLatexFormatter();
365        File.WriteAllText(filename, formatter.Format(Tree));
366      }
367    }
368    #endregion
369
370    private void reingoldTilfordToolStripMenuItem_Click(object sender, EventArgs e) {
371      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> {
372        NodeWidth = preferredNodeWidth,
373        NodeHeight = preferredNodeHeight,
374        HorizontalSpacing = minHorizontalDistance,
375        VerticalSpacing = minVerticalDistance
376      };
377      Repaint();
378    }
379
380    private void boxesToolStripMenuItem_Click(object sender, EventArgs e) {
381      layoutEngine = new BoxesLayoutEngine<ISymbolicExpressionTreeNode> {
382        NodeWidth = preferredNodeWidth,
383        NodeHeight = preferredNodeHeight,
384        HorizontalSpacing = minHorizontalDistance,
385        VerticalSpacing = minVerticalDistance
386      };
387      Repaint();
388    }
389  }
390}
Note: See TracBrowser for help on using the repository browser.