Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 10561 was 10561, checked in by bburlacu, 10 years ago

#2076: Updated the way the layout is used in the SymbolicExpressionTreeChart; updated simplifier view accordingly.

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