Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionaryTracking/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 9963

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

#1772: Merged changes from the trunk and other branches. Added new ExtendedSymbolicExpressionTreeCanvas control for the visual exploration of tree genealogies. Reorganized some files and folders.

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