Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.ReingoldTilfordTreeLayout/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 12931

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

#2076: Fixed image save to file in the SymbolicExpressionTreeChart. Removed commented code from SymbolicExpressionTreeLayoutAdapter.

File size: 17.2 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;
[9970]26using System.IO;
27using System.Linq;
[3244]28using System.Windows.Forms;
[9970]29using Point = System.Drawing.Point;
[3244]30
31namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
[8942]32  public partial class SymbolicExpressionTreeChart : UserControl {
[3470]33    private Image image;
[10434]34    private readonly StringFormat stringFormat;
[5513]35    private Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode> visualTreeNodes;
[6803]36    private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection> visualLines;
[9970]37    private readonly ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> layoutEngine;
38    private readonly SymbolicExpressionTreeLayoutAdapter layoutAdapter;
[3244]39
[9970]40    private const int preferredNodeWidth = 70;
41    private const int preferredNodeHeight = 46;
42    private const int minHorizontalDistance = 20;
43    private const int minVerticalDistance = 20;
44
45
[3244]46    public SymbolicExpressionTreeChart() {
47      InitializeComponent();
[3470]48      this.image = new Bitmap(Width, Height);
[8942]49      this.stringFormat = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
[3244]50      this.spacing = 5;
51      this.lineColor = Color.Black;
52      this.backgroundColor = Color.White;
[10434]53      this.textFont = new Font(FontFamily.GenericSansSerif, 12);
[9970]54      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>();
55      layoutAdapter = new SymbolicExpressionTreeLayoutAdapter();
[3244]56    }
57
[5513]58    public SymbolicExpressionTreeChart(ISymbolicExpressionTree tree)
[3244]59      : this() {
[9970]60      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>();
61      layoutAdapter = new SymbolicExpressionTreeLayoutAdapter();
[3244]62      this.Tree = tree;
63    }
64
[9970]65    #region Public properties
[3244]66    private int spacing;
67    public int Spacing {
68      get { return this.spacing; }
69      set {
70        this.spacing = value;
71        this.Repaint();
72      }
73    }
74
75    private Color lineColor;
76    public Color LineColor {
77      get { return this.lineColor; }
78      set {
79        this.lineColor = value;
80        this.Repaint();
81      }
82    }
83
84    private Color backgroundColor;
85    public Color BackgroundColor {
86      get { return this.backgroundColor; }
87      set {
88        this.backgroundColor = value;
89        this.Repaint();
90      }
91    }
92
93    private Font textFont;
94    public Font TextFont {
95      get { return this.textFont; }
96      set {
97        this.textFont = value;
98        this.Repaint();
99      }
100    }
101
[5513]102    private ISymbolicExpressionTree tree;
103    public ISymbolicExpressionTree Tree {
[3244]104      get { return this.tree; }
105      set {
106        tree = value;
[5513]107        visualTreeNodes = new Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode>();
[6803]108        visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection>();
[3244]109        if (tree != null) {
[6803]110          foreach (ISymbolicExpressionTreeNode node in tree.IterateNodesPrefix()) {
[3244]111            visualTreeNodes[node] = new VisualSymbolicExpressionTreeNode(node);
[6803]112            if (node.Parent != null) visualLines[Tuple.Create(node.Parent, node)] = new VisualSymbolicExpressionTreeNodeConnection();
113          }
[3244]114        }
115        Repaint();
116      }
117    }
118
[6803]119    private bool suspendRepaint;
120    public bool SuspendRepaint {
121      get { return suspendRepaint; }
122      set { suspendRepaint = value; }
123    }
[9970]124    #endregion
[6803]125
[3470]126    protected override void OnPaint(PaintEventArgs e) {
[6803]127      e.Graphics.DrawImage(image, 0, 0);
[3470]128      base.OnPaint(e);
129    }
130    protected override void OnResize(EventArgs e) {
131      base.OnResize(e);
[5956]132      if (this.Width <= 1 || this.Height <= 1)
[3470]133        this.image = new Bitmap(1, 1);
134      else
135        this.image = new Bitmap(Width, Height);
136      this.Repaint();
137    }
[3244]138
139    public void Repaint() {
[6803]140      if (!suspendRepaint) {
141        this.GenerateImage();
142        this.Refresh();
143      }
[3470]144    }
145
[8980]146    public void RepaintNodes() {
147      if (!suspendRepaint) {
[8986]148        using (var graphics = Graphics.FromImage(image)) {
149          graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
150          graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
151          foreach (var visualNode in visualTreeNodes.Values) {
152            DrawTreeNode(graphics, visualNode);
153          }
[8980]154        }
155        this.Refresh();
156      }
157    }
158
159    public void RepaintNode(VisualSymbolicExpressionTreeNode visualNode) {
160      if (!suspendRepaint) {
[8986]161        using (var graphics = Graphics.FromImage(image)) {
162          graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
163          graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
164          DrawTreeNode(graphics, visualNode);
165        }
[8980]166        this.Refresh();
167      }
168    }
169
[3470]170    private void GenerateImage() {
171      using (Graphics graphics = Graphics.FromImage(image)) {
[3244]172        graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
173        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
174        graphics.Clear(backgroundColor);
175        if (tree != null) {
[9970]176          DrawFunctionTree(tree, graphics, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
[3244]177        }
178      }
179    }
180
[5513]181    public VisualSymbolicExpressionTreeNode GetVisualSymbolicExpressionTreeNode(ISymbolicExpressionTreeNode symbolicExpressionTreeNode) {
[3915]182      if (visualTreeNodes.ContainsKey(symbolicExpressionTreeNode))
183        return visualTreeNodes[symbolicExpressionTreeNode];
184      return null;
185    }
186
[6803]187    public VisualSymbolicExpressionTreeNodeConnection GetVisualSymbolicExpressionTreeNodeConnection(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
188      if (child.Parent != parent) throw new ArgumentException();
189      var key = Tuple.Create(parent, child);
190      VisualSymbolicExpressionTreeNodeConnection connection = null;
191      visualLines.TryGetValue(key, out connection);
192      return connection;
193    }
194
[3244]195    #region events
196    public event MouseEventHandler SymbolicExpressionTreeNodeClicked;
[8942]197    protected virtual void OnSymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) {
[3244]198      var clicked = SymbolicExpressionTreeNodeClicked;
199      if (clicked != null)
200        clicked(sender, e);
201    }
202
[9043]203    protected virtual void SymbolicExpressionTreeChart_MouseClick(object sender, MouseEventArgs e) {
[3244]204      VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
[8942]205      if (visualTreeNode != null) {
[3244]206        OnSymbolicExpressionTreeNodeClicked(visualTreeNode, e);
[8942]207      }
[3244]208    }
209
210    public event MouseEventHandler SymbolicExpressionTreeNodeDoubleClicked;
[8942]211    protected virtual void OnSymbolicExpressionTreeNodeDoubleClicked(object sender, MouseEventArgs e) {
[3244]212      var doubleClicked = SymbolicExpressionTreeNodeDoubleClicked;
213      if (doubleClicked != null)
214        doubleClicked(sender, e);
215    }
216
[9043]217    protected virtual void SymbolicExpressionTreeChart_MouseDoubleClick(object sender, MouseEventArgs e) {
[3244]218      VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
219      if (visualTreeNode != null)
220        OnSymbolicExpressionTreeNodeDoubleClicked(visualTreeNode, e);
221    }
222
223    public event ItemDragEventHandler SymbolicExpressionTreeNodeDrag;
[8942]224    protected virtual void OnSymbolicExpressionTreeNodeDragDrag(object sender, ItemDragEventArgs e) {
[3244]225      var dragged = SymbolicExpressionTreeNodeDrag;
226      if (dragged != null)
227        dragged(sender, e);
228    }
229
230    private VisualSymbolicExpressionTreeNode draggedSymbolicExpressionTree;
231    private MouseButtons dragButtons;
232    private void SymbolicExpressionTreeChart_MouseDown(object sender, MouseEventArgs e) {
233      this.dragButtons = e.Button;
234      this.draggedSymbolicExpressionTree = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
235    }
236    private void SymbolicExpressionTreeChart_MouseUp(object sender, MouseEventArgs e) {
237      this.draggedSymbolicExpressionTree = null;
238      this.dragButtons = MouseButtons.None;
239    }
240
241    private void SymbolicExpressionTreeChart_MouseMove(object sender, MouseEventArgs e) {
242      VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
243      if (draggedSymbolicExpressionTree != null &&
244        draggedSymbolicExpressionTree != visualTreeNode) {
245        OnSymbolicExpressionTreeNodeDragDrag(draggedSymbolicExpressionTree, new ItemDragEventArgs(dragButtons, draggedSymbolicExpressionTree));
246        draggedSymbolicExpressionTree = null;
247      } else if (draggedSymbolicExpressionTree == null &&
248        visualTreeNode != null) {
249        string tooltipText = visualTreeNode.ToolTip;
250        if (this.toolTip.GetToolTip(this) != tooltipText)
251          this.toolTip.SetToolTip(this, tooltipText);
252
253      } else if (visualTreeNode == null)
254        this.toolTip.SetToolTip(this, "");
255    }
256
[6803]257    public VisualSymbolicExpressionTreeNode FindVisualSymbolicExpressionTreeNodeAt(int x, int y) {
[3244]258      foreach (var visualTreeNode in visualTreeNodes.Values) {
259        if (x >= visualTreeNode.X && x <= visualTreeNode.X + visualTreeNode.Width &&
260            y >= visualTreeNode.Y && y <= visualTreeNode.Y + visualTreeNode.Height)
261          return visualTreeNode;
262      }
263      return null;
264    }
265    #endregion
266
267    #region methods for painting the symbolic expression tree
[10487]268    private void DrawFunctionTree(ISymbolicExpressionTree symbolicExpressionTree, Graphics graphics, int preferredWidth, int preferredHeight, int minHDistance, int minVDistance) {
269      var layoutNodes = layoutAdapter.Convert(symbolicExpressionTree).ToList();
[9970]270      layoutEngine.Reset();
271      layoutEngine.Root = layoutNodes[0];
[10471]272      layoutEngine.AddNodes(layoutNodes);
[10487]273      layoutEngine.MinHorizontalSpacing = (preferredWidth + minHDistance);
274      layoutEngine.MinVerticalSpacing = (preferredHeight + minVDistance);
[9970]275      layoutEngine.CalculateLayout();
276      var bounds = layoutEngine.Bounds();
[3244]277
[10487]278      double verticalScalingFactor = 1.0;
279      double layoutHeight = (bounds.Height + preferredHeight);
280      if (this.Height < layoutHeight)
281        verticalScalingFactor = this.Height / layoutHeight;
[9993]282
[10487]283      double horizontalScalingFactor = 1.0;
284      double layoutWidth = (bounds.Width + preferredWidth);
285      if (this.Width < layoutWidth)
286        horizontalScalingFactor = this.Width / layoutWidth;
287
288      double horizontalOffset;
289      if (this.Width > layoutWidth)
290        horizontalOffset = (this.Width - layoutWidth) / 2.0;
291      else
292        horizontalOffset = preferredWidth / 2.0;
293
294      var levels = layoutNodes.GroupBy(n => n.Level, n => n).ToList();
295      for (int i = levels.Count - 1; i >= 0; --i) {
296        var nodes = levels[i].ToList();
297
298        double minSpacing = double.MaxValue;
299        if (nodes.Count > 1) {
300          for (int j = 1; j < nodes.Count() - 1; ++j) {
301            var distance = nodes[j].X - nodes[j - 1].X; // guaranteed to be > 0
302            if (minSpacing > distance) minSpacing = distance;
303          }
[9993]304        }
[10487]305        minSpacing *= horizontalScalingFactor;
[9993]306
[10487]307        int minWidth = (int)Math.Round(preferredWidth * horizontalScalingFactor);
308        int width = (int)Math.Min(minSpacing, preferredWidth) - 2; // leave some pixels so that node margins don't overlap
309
310        foreach (var layoutNode in nodes) {
[9993]311          var visualNode = visualTreeNodes[layoutNode.Content];
[10487]312          visualNode.Width = width;
313          visualNode.Height = (int)Math.Round(preferredHeight * verticalScalingFactor);
314          visualNode.X = (int)Math.Round((layoutNode.X + horizontalOffset) * horizontalScalingFactor + (minWidth - width) / 2.0);
315          visualNode.Y = (int)Math.Round(layoutNode.Y * verticalScalingFactor);
[9970]316          DrawTreeNode(graphics, visualNode);
[8986]317        }
[9970]318      }
[9993]319      // draw node connections
[9970]320      foreach (var visualNode in visualTreeNodes.Values) {
321        var node = visualNode.SymbolicExpressionTreeNode;
322        foreach (var subtree in node.Subtrees) {
323          var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node, subtree);
324          var visualSubtree = visualTreeNodes[subtree];
325          var origin = new Point(visualNode.X + visualNode.Width / 2, visualNode.Y + visualNode.Height);
326          var target = new Point(visualSubtree.X + visualSubtree.Width / 2, visualSubtree.Y);
[10487]327          graphics.Clip = new Region(new Rectangle(Math.Min(origin.X, target.X), origin.Y, Math.Max(origin.X, target.X), target.Y));
[9970]328          using (var linePen = new Pen(visualLine.LineColor)) {
[8986]329            linePen.DashStyle = visualLine.DashStyle;
[9970]330            graphics.DrawLine(linePen, origin, target);
[8986]331          }
[6803]332        }
[3244]333      }
334    }
[8980]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;
[8986]348      using (var textBrush = new SolidBrush(visualTreeNode.TextColor))
349      using (var nodeLinePen = new Pen(visualTreeNode.LineColor))
350      using (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);
[8980]362      }
363    }
[3244]364    #endregion
[4651]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
[9587]375    public void SaveImageAsBitmap(string filename) {
[4651]376      if (tree == null) return;
377      Image image = new Bitmap(Width, Height);
378      using (Graphics g = Graphics.FromImage(image)) {
[10487]379        DrawFunctionTree(tree, g, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
[4651]380      }
381      image.Save(filename);
382    }
383
[9587]384    public void SaveImageAsEmf(string filename) {
[4651]385      if (tree == null) return;
386      using (Graphics g = CreateGraphics()) {
387        using (Metafile file = new Metafile(filename, g.GetHdc())) {
388          using (Graphics emfFile = Graphics.FromImage(file)) {
[10487]389            DrawFunctionTree(tree, emfFile, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
[4651]390          }
391        }
392        g.ReleaseHdc();
393      }
394    }
395    #endregion
[9970]396    #region export pgf/tikz
397    private void exportLatexToolStripMenuItem_Click(object sender, EventArgs e) {
398      using (var dialog = new SaveFileDialog { Filter = "Tex (*.tex)|*.tex" }) {
399        if (dialog.ShowDialog() != DialogResult.OK) return;
400        string filename = dialog.FileName.ToLower();
401        var formatter = new SymbolicExpressionTreeLatexFormatter();
402        File.WriteAllText(filename, formatter.Format(Tree));
403      }
404    }
405    #endregion
[3244]406  }
407}
Note: See TracBrowser for help on using the repository browser.