Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Refactored subtree tracing functionality and moved it from the view into a separate static class. Shortened symbol names in the FragmentGraphView so that trees are displayed more nicely.

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