Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TreeLayout/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs @ 9650

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

#2076: Created branch and committed first version of the implementation.

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