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

Last change on this file since 9854 was 9854, checked in by bburlacu, 9 years ago

#2076: Wrapped dialog creation for pgf/tikz export in an using statement.

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