Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionTracking/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeTile.cs @ 10797

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

#1772: Added text labels to SymbolicExpressionTreeTiles so that the generation number is also displayed.

File size: 9.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 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.Drawing2D;
26using System.Drawing.Text;
27using System.Linq;
28using HeuristicLab.Common;
29using HeuristicLab.Visualization;
30
31using Rectangle = HeuristicLab.Visualization.Rectangle;
32
33namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
34  /// <summary>
35  /// The SymbolicExpressionTreeTile will display either a full tree or just a subtree (case in which the SymbolicExpressionTreeProperty remains unset).
36  /// By setting the Root property, the user can use the SymbolicExpressionTreeTile to layout and display arbitrary subtrees.
37  /// </summary>
38  public class SymbolicExpressionTreeTile : Group {
39    public int PreferredNodeWidth { get; set; }
40    public int PreferredNodeHeight { get; set; }
41
42    private const int labelHeight = 30;
43
44    public ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> LayoutEngine { get; set; }
45    private readonly Dictionary<IPrimitive, ISymbolicExpressionTreeNode> primitivesToNodes;
46    private readonly Dictionary<ISymbolicExpressionTreeNode, IPrimitive> nodesToPrimitives;
47
48    private string label;
49    public string Label {
50      get { return label; }
51      set { label = value; }
52    }
53
54    private Size size;
55    public Size Size {
56      private set { size = value; }
57      get { return size; }
58    }
59
60    private Point position;
61    public Point Position {
62      get { return position; }
63      set {
64        var oldpos = position;
65        position = value;
66        int ox = position.X - oldpos.X;
67        int oy = position.Y - oldpos.Y;
68        // translate all primitives to the new position
69        foreach (var p in Primitives) {
70          var rpb = p as RectangularPrimitiveBase;
71          if (rpb != null) {
72            var lowerLeft = new Point((int)Math.Floor(rpb.LowerLeft.X) + ox, (int)Math.Floor(rpb.LowerLeft.Y + oy));
73            var upperRight = new PointD(lowerLeft.X + rpb.Size.Width, lowerLeft.Y + rpb.Size.Height);
74            rpb.SetPosition(lowerLeft, upperRight);
75          }
76          var line = p as LinearPrimitiveBase;
77          if (line != null) {
78            var start = new Point((int)Math.Floor(line.Start.X) + ox, (int)Math.Floor(line.Start.Y) + oy);
79            var end = new Point(start.X + (int)line.Size.Width, start.Y + (int)line.Size.Height);
80            line.SetPosition(start, end);
81          }
82        }
83      }
84    }
85
86    public IPrimitive GetPrimitive(ISymbolicExpressionTreeNode node) {
87      IPrimitive primitive;
88      nodesToPrimitives.TryGetValue(node, out primitive);
89      return primitive;
90    }
91
92    public ISymbolicExpressionTreeNode GetExpressionTreeNode(IPrimitive primitive) {
93      ISymbolicExpressionTreeNode node;
94      primitivesToNodes.TryGetValue(primitive, out node);
95      return node;
96    }
97
98    private ISymbolicExpressionTree symbolicExpressionTree;
99    public ISymbolicExpressionTree SymbolicExpressionTree {
100      get { return symbolicExpressionTree; }
101      set {
102        symbolicExpressionTree = value;
103        Root = symbolicExpressionTree.Root;
104      }
105    }
106
107    new private void Clear() {
108      Group.Clear();
109      primitivesToNodes.Clear();
110      nodesToPrimitives.Clear();
111    }
112
113    private ISymbolicExpressionTreeNode root;
114    public ISymbolicExpressionTreeNode Root {
115      get { return root; }
116      set {
117        root = value;
118        GeneratePrimitives(PreferredNodeWidth, PreferredNodeHeight);
119      }
120    }
121
122    public SymbolicExpressionTreeTile(IChart chart)
123      : base(chart) {
124      primitivesToNodes = new Dictionary<IPrimitive, ISymbolicExpressionTreeNode>();
125      nodesToPrimitives = new Dictionary<ISymbolicExpressionTreeNode, IPrimitive>();
126      PreferredNodeWidth = 80;
127      PreferredNodeHeight = 40;
128      Group = new Group(chart);
129    }
130    public SymbolicExpressionTreeTile(IChart chart, ISymbolicExpressionTree tree)
131      : this(chart) {
132      SymbolicExpressionTree = tree;
133    }
134    private void GeneratePrimitives(double preferredNodeWidth, double preferredNodeHeight) {
135      Clear();
136      var actualRoot = Root;
137      if (Root.Symbol is ProgramRootSymbol && Root.SubtreeCount == 1) { actualRoot = Root.GetSubtree(0); }
138
139      LayoutEngine.NodeWidth = PreferredNodeWidth;
140      LayoutEngine.NodeHeight = PreferredNodeHeight;
141
142      var visualNodes = LayoutEngine.CalculateLayout(actualRoot).ToList();
143
144      var font = new Font(FontFamily.GenericSansSerif, 10, GraphicsUnit.Pixel);
145
146      var visualNodeMap = visualNodes.ToDictionary(x => x.Content, x => x);
147
148      foreach (var visualNode in visualNodes) {
149        var lowerLeft = new PointD(visualNode.X, visualNode.Y);
150        var upperRight = new PointD(visualNode.X + preferredNodeWidth, visualNode.Y + preferredNodeHeight);
151        var node = visualNode.Content;
152
153        RectangularPrimitiveBase rectangularPrimitive;
154        var shortenedLabel = ShortenLabel(node);
155        if (node.SubtreeCount == 0) {
156          rectangularPrimitive = new Rectangle(Chart, lowerLeft, upperRight) { Font = font, Text = shortenedLabel };
157        } else {
158          rectangularPrimitive = new Ellipse(Chart, lowerLeft, upperRight) { Font = font, Text = shortenedLabel };
159        }
160
161        primitivesToNodes.Add(rectangularPrimitive, node); // to be able to retrieve nodes via primitives
162        nodesToPrimitives.Add(node, rectangularPrimitive);
163        this.Add(rectangularPrimitive);
164
165        if (rectangularPrimitive.Size.Width.IsAlmost(0) || rectangularPrimitive.Size.Height.IsAlmost(0)) {
166          throw new Exception("Primitive size cannot be zero.");
167        }
168      }
169
170      foreach (var node in visualNodes.Where(n => n.Content.SubtreeCount > 0)) {
171        var parent = node.Content;
172        foreach (var child in parent.Subtrees.Select(x => visualNodeMap[x])) {
173          var start = new PointD(node.X + preferredNodeWidth / 2, node.Y + preferredNodeHeight);
174          var end = new PointD(child.X + preferredNodeWidth / 2, child.Y);
175          var line = new Line(this.Chart, start, end);
176          this.Add(line);
177        }
178      }
179      int xmin = 0, ymin = 0, xmax = 0, ymax = 0;
180      foreach (var rpb in Primitives.OfType<RectangularPrimitiveBase>()) {
181        if (xmin > rpb.LowerLeft.X) xmin = (int)Math.Floor(rpb.LowerLeft.X);
182        if (xmax < rpb.UpperRight.X) xmax = (int)Math.Ceiling(rpb.UpperRight.X);
183        if (ymin > rpb.LowerLeft.Y) ymin = (int)Math.Floor(rpb.LowerLeft.Y);
184        if (ymax < rpb.UpperRight.Y) ymax = (int)Math.Ceiling(rpb.UpperRight.Y);
185      }
186      int width = xmax - xmin;
187      int height = ymax - ymin;
188
189      // draw a primitive to display the label
190      var labelRect = new Rectangle(this.Chart, new PointD(0, height), new PointD(width, height + labelHeight)) {
191        Pen = new Pen(Color.Black),
192        Brush = new SolidBrush(Color.Transparent),
193        Text = Label,
194        Font = new Font(FontFamily.GenericSansSerif, 12)
195      };
196      this.Add(labelRect);
197
198      // draw a rectangle to mark the countour of this tile
199      Size = new Size(width, height + labelHeight);
200
201      var rectangle = new Rectangle(this.Chart, new PointD(0, 0), new PointD(Size.Width, Size.Height)) {
202        Pen = new Pen(Color.Gray),
203        Brush = new SolidBrush(Color.Transparent)
204      };
205      this.Add(rectangle);
206
207      // flip primitives vertically
208      foreach (var primitive in Primitives) {
209        var rpb = primitive as RectangularPrimitiveBase;
210        if (rpb != null) {
211          rpb.SetPosition(rpb.LowerLeft.X, Size.Height - rpb.UpperRight.Y, rpb.UpperRight.X, Size.Height - rpb.LowerLeft.Y);
212
213        } else {
214          var line = primitive as LinearPrimitiveBase;
215          if (line != null) {
216            line.SetPosition(line.Start.X, Size.Height - line.Start.Y, line.End.X, Size.Height - line.End.Y);
217          }
218        }
219      }
220    }
221
222    public override void Draw(Graphics graphics) {
223      graphics.SmoothingMode = SmoothingMode.HighQuality;
224      graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
225      base.Draw(graphics);
226    }
227
228    private string ShortenLabel(ISymbolicExpressionTreeNode node) {
229      var term = node as SymbolicExpressionTreeTerminalNode;
230      if (term != null) {
231        var parts = term.ToString().Split(' '); // split by space
232        if (parts.Length == 1) {
233          return parts[0].Substring(0, 5);
234        } else {
235          return parts[0].Substring(0, 5) + parts[1];
236        }
237      }
238      return node.Symbol.ToString().Substring(0, 3);
239    }
240  }
241}
Note: See TracBrowser for help on using the repository browser.