Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Adapted visualization code according to the changes in the HeuristicLab.Visualization branch.

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