Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Fixed another minor display bug concerning elite individuals. Fixed bug when saving fragments from mutation. Displayed quality as well in the SymbolicExpressionTreeTile.

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