Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
03/07/14 16:23:41 (11 years ago)
Author:
mkommend
Message:

#2076: Simplified the API of the tree layout engines.

Location:
trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/LayoutEngines
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/LayoutEngines/BoxesLayoutEngine.cs

    r10561 r10565  
    1 
     1#region License Information
     2
     3/* HeuristicLab
     4 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
     5 *
     6 * This file is part of HeuristicLab.
     7 *
     8 * HeuristicLab is free software: you can redistribute it and/or modify
     9 * it under the terms of the GNU General Public License as published by
     10 * the Free Software Foundation, either version 3 of the License, or
     11 * (at your option) any later version.
     12 *
     13 * HeuristicLab is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 * GNU General Public License for more details.
     17 *
     18 * You should have received a copy of the GNU General Public License
     19 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
     20 */
     21
     22#endregion
     23
    224using System;
    325using System.Collections.Generic;
    4 using System.Drawing;
    526using System.Linq;
    627
    728namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
    829  public class BoxesLayoutEngine<T> : ILayoutEngine<T> where T : class {
    9     private readonly Dictionary<T, VisualTreeNode<T>> nodeMap;
    10 
    1130    public int NodeWidth { get; set; }
    1231    public int NodeHeight { get; set; }
    1332    public int HorizontalSpacing { get; set; }
    1433    public int VerticalSpacing { get; set; }
    15     private VisualTreeNode<T> layoutRoot;
    1634
    17     public int Width { get; private set; }
    18     public int Height { get; private set; }
     35    private readonly Func<T, IEnumerable<T>> GetChildren;
     36    private readonly Func<T, int> GetLength;
     37    private readonly Func<T, int> GetDepth;
    1938
    20     public Func<T, IEnumerable<T>> GetChildren { get; set; }
    21     public Func<T, int> GetLength { get; set; }
    22     public Func<T, int> GetDepth { get; set; }
     39    public BoxesLayoutEngine(Func<T, IEnumerable<T>> GetChildren, Func<T, int> GetLength, Func<T, int> GetDepth) {
     40      if (GetChildren == null) throw new ArgumentNullException("GetChildren");
     41      if (GetLength == null) throw new ArgumentNullException("GetLength");
     42      if (GetDepth == null) throw new ArgumentNullException("GetDepth");
    2343
    24     public BoxesLayoutEngine() {
    25       nodeMap = new Dictionary<T, VisualTreeNode<T>>();
     44      this.GetChildren = GetChildren;
     45      this.GetLength = GetLength;
     46      this.GetDepth = GetDepth;
    2647    }
    2748
    28     public void CalculateLayout() {
    29       throw new Exception("The BoxesLayoutEngine does not support arbitrary bounds. Please use method CalculateLayout(Width, Height)");
     49
     50    public IEnumerable<VisualTreeNode<T>> CalculateLayout(T root, float width, float height) {
     51      var nodeMap = new Dictionary<T, VisualTreeNode<T>>();
     52      CreateVisualNodes(root, nodeMap);
     53      RecursiveLayout(nodeMap, nodeMap[root], 0, 0, (int)Math.Round(width), (int)Math.Round(height) / GetDepth(root));
     54      return nodeMap.Values;
    3055    }
    3156
    32     public void CalculateLayout(float width, float height) {
    33       Width = (int)Math.Round(width);
    34       Height = (int)Math.Round(height);
    35       Reset();
    36       RecursiveLayout(layoutRoot, 0, 0, Width, Height / GetDepth(layoutRoot.Content));
    37     }
    38 
    39     public void Initialize(T root, Func<T, IEnumerable<T>> getChildren, Func<T, int> getLength, Func<T, int> getDepth) {
    40       if (getChildren == null || getLength == null || getDepth == null)
    41         throw new ArgumentNullException("The BoxesLayoutEngine requires all of the lambdas: (getChildren, getLength and getDepth) to be defined.");
    42       GetChildren = getChildren;
    43       GetLength = getLength;
    44       GetDepth = getDepth;
    45       Clear();
    46       Expand(root); // produce the nodeMap
    47       layoutRoot = nodeMap[root];
    48     }
    49 
    50     private void Expand(T root) {
     57    private void CreateVisualNodes(T root, Dictionary<T, VisualTreeNode<T>> map) {
    5158      var node = new VisualTreeNode<T>(root) {
    5259        PreferredWidth = NodeWidth,
    5360        PreferredHeight = NodeHeight
    5461      };
    55       nodeMap.Add(root, node);
     62
     63      map.Add(root, node);
    5664      var children = GetChildren(root).ToList();
    5765      if (children.Any()) {
    5866        foreach (var child in children) {
    59           Expand(child);
     67          CreateVisualNodes(child, map);
    6068        }
    6169      }
    6270    }
    6371
    64     public void Center(float width, float height) {
    65       // does nothing because the BoxesLayout centers the tree by default
    66     }
    67 
    68     public void Clear() {
    69       nodeMap.Clear();
    70       layoutRoot = null;
    71     }
    72 
    73     public void Reset() {
    74       foreach (var node in nodeMap.Values) {
    75         node.X = 0;
    76         node.Y = 0;
    77       }
    78     }
    79 
    80     public Dictionary<T, PointF> GetCoordinates() {
    81       return nodeMap.ToDictionary(x => x.Key, x => new PointF(x.Value.X, x.Value.Y));
    82     }
    83 
    84     public void FitToBounds(float width, float height) {
    85       // does nothing because the BoxesLayout is by default stretched on the whole drawing area
    86     }
    87 
    88     private void RecursiveLayout(VisualTreeNode<T> visualTreeNode, int x, int y, int width, int height) {
     72    private void RecursiveLayout(Dictionary<T, VisualTreeNode<T>> nodeMap, VisualTreeNode<T> visualTreeNode, int x, int y, int width, int height) {
    8973      float center_x = x + width / 2;
    9074      float center_y = y + height / 2;
     
    127111      for (int i = 0; i < children.Count; i++) {
    128112        xBoundaries[i + 1] = (int)(xBoundaries[i] + (width * (double)GetLength(children[i])) / (GetLength(node) - 1));
    129         RecursiveLayout(nodeMap[children[i]], xBoundaries[i], y + height, xBoundaries[i + 1] - xBoundaries[i], height);
     113        RecursiveLayout(nodeMap, nodeMap[children[i]], xBoundaries[i], y + height, xBoundaries[i + 1] - xBoundaries[i], height);
    130114      }
    131     }
    132 
    133     public IEnumerable<T> GetContentNodes() {
    134       return nodeMap.Keys;
    135     }
    136 
    137     public IEnumerable<VisualTreeNode<T>> GetVisualNodes() {
    138       return nodeMap.Values;
    139115    }
    140116  }
  • trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/LayoutEngines/ILayoutEngine.cs

    r10561 r10565  
    1 
    2 using System;
     1#region License Information
     2
     3/* HeuristicLab
     4 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
     5 *
     6 * This file is part of HeuristicLab.
     7 *
     8 * HeuristicLab is free software: you can redistribute it and/or modify
     9 * it under the terms of the GNU General Public License as published by
     10 * the Free Software Foundation, either version 3 of the License, or
     11 * (at your option) any later version.
     12 *
     13 * HeuristicLab is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 * GNU General Public License for more details.
     17 *
     18 * You should have received a copy of the GNU General Public License
     19 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
     20 */
     21
     22#endregion
     23
    324using System.Collections.Generic;
    4 using System.Drawing;
    525
    626namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
     
    1131    int VerticalSpacing { get; set; }
    1232
    13     void CalculateLayout();
    14     void CalculateLayout(float width, float height);
    15     void Initialize(T root, Func<T, IEnumerable<T>> getChildren, Func<T, int> getLength = null, Func<T, int> getDepth = null);
    16     void Clear();
    17     void Reset();
    18 
    19     // function members necessary to navigate the tree structure
    20     Func<T, IEnumerable<T>> GetChildren { get; set; }
    21     Func<T, int> GetLength { get; set; }
    22     Func<T, int> GetDepth { get; set; }
    23 
    24     IEnumerable<T> GetContentNodes();
    25     IEnumerable<VisualTreeNode<T>> GetVisualNodes();
    26     Dictionary<T, PointF> GetCoordinates();
     33    IEnumerable<VisualTreeNode<T>> CalculateLayout(T root, float width, float height);
    2734  }
    2835}
  • trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/LayoutEngines/LayoutNode.cs

    r10520 r10565  
    2525
    2626namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
    27   public class LayoutNode<T> : object where T : class {
     27  internal class LayoutNode<T> : object where T : class {
    2828    public float Width { get; set; }
    2929    public float Height { get; set; }
  • trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/LayoutEngines/ReingoldTilfordLayoutEngine.cs

    r10561 r10565  
    77namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
    88  public class ReingoldTilfordLayoutEngine<T> : ILayoutEngine<T> where T : class {
    9     private readonly Dictionary<T, LayoutNode<T>> nodeMap; // provides a reverse mapping T => LayoutNode
    109    public int NodeWidth { get; set; }
    1110    public int NodeHeight { get; set; }
     
    2221    }
    2322
    24     public Func<T, IEnumerable<T>> GetChildren { get; set; }
    25     public Func<T, int> GetLength { get; set; }
    26     public Func<T, int> GetDepth { get; set; }
    27     private LayoutNode<T> layoutRoot;
    28 
    29     public ReingoldTilfordLayoutEngine() {
    30       nodeMap = new Dictionary<T, LayoutNode<T>>();
    31     }
    32 
    33     public ReingoldTilfordLayoutEngine(T root, Func<T, IEnumerable<T>> childrenFunc)
    34       : this() {
    35       Initialize(root, childrenFunc);
    36     }
    37 
    38     public void Initialize(T root, Func<T, IEnumerable<T>> getChildren, Func<T, int> getLength = null, Func<T, int> getDepth = null) {
    39       GetChildren = getChildren;
    40       Clear();
    41       var node = new LayoutNode<T> { Content = root, Width = NodeWidth, Height = NodeHeight };
    42       node.Ancestor = node;
    43       layoutRoot = node;
    44       Expand(node);
    45     }
    46 
    47     private void Expand(LayoutNode<T> lRoot) {
    48       nodeMap.Add(lRoot.Content, lRoot);
     23    private readonly Func<T, IEnumerable<T>> GetChildren;
     24
     25    public ReingoldTilfordLayoutEngine(Func<T, IEnumerable<T>> GetChildren) {
     26      this.GetChildren = GetChildren;
     27    }
     28
     29    public IEnumerable<VisualTreeNode<T>> CalculateLayout(T root) {
     30      return CalculateLayout(root, 0, 0);
     31    }
     32
     33    public IEnumerable<VisualTreeNode<T>> CalculateLayout(T root, float width, float height) {
     34      Dictionary<T, LayoutNode<T>> layoutNodeMap = new Dictionary<T, LayoutNode<T>>();
     35      var layoutRoot = new LayoutNode<T> { Content = root, Width = NodeWidth, Height = NodeHeight, };
     36      layoutRoot.Ancestor = layoutRoot;
     37      Expand(layoutRoot, layoutNodeMap);
     38
     39      FirstWalk(layoutRoot);
     40      SecondWalk(layoutRoot, -layoutRoot.Prelim);
     41      NormalizeCoordinates(layoutNodeMap.Values);
     42      if (height != 0 && width != 0) {
     43        FitToBounds(width, height, layoutNodeMap.Values);
     44        Center(width, height, layoutNodeMap.Values);
     45      }
     46
     47      return layoutNodeMap.Values.Select(x => new VisualTreeNode<T>(x.Content) {
     48        Width = (int)Math.Round(x.Width),
     49        Height = (int)Math.Round(x.Height),
     50        X = (int)Math.Round(x.X),
     51        Y = (int)Math.Round(x.Y)
     52      });
     53    }
     54
     55    private void Expand(LayoutNode<T> lRoot, Dictionary<T, LayoutNode<T>> map) {
     56      map.Add(lRoot.Content, lRoot);
    4957      var children = GetChildren(lRoot.Content).ToList();
    5058      if (!children.Any()) return;
     
    6169        node.Ancestor = node;
    6270        lRoot.Children.Add(node);
    63         Expand(node);
    64       }
    65     }
    66 
    67     public IEnumerable<T> GetContentNodes() {
    68       return nodeMap.Keys;
    69     }
    70 
    71     public IEnumerable<VisualTreeNode<T>> GetVisualNodes() {
    72       return nodeMap.Values.Select(x => new VisualTreeNode<T>(x.Content) {
    73         Width = (int)Math.Round(x.Width),
    74         Height = (int)Math.Round(x.Height),
    75         X = (int)Math.Round(x.X),
    76         Y = (int)Math.Round(x.Y)
    77       });
    78     }
    79 
    80     public IEnumerable<LayoutNode<T>> GetLayoutNodes() {
    81       return nodeMap.Values;
    82     }
    83 
    84     public void AddNode(T content) {
    85       if (nodeMap.ContainsKey(content)) { throw new ArgumentException("Content already present in the dictionary."); }
    86       var node = new LayoutNode<T> { Content = content };
    87       nodeMap.Add(content, node);
    88     }
    89 
    90     public void AddNode(LayoutNode<T> node) {
    91       var content = node.Content;
    92       if (nodeMap.ContainsKey(content)) { throw new ArgumentException("Content already present in the dictionary."); }
    93       nodeMap.Add(content, node);
    94     }
    95 
    96     public void AddNodes(IEnumerable<LayoutNode<T>> nodes) {
    97       foreach (var node in nodes)
    98         nodeMap.Add(node.Content, node);
    99     }
    100 
    101     public LayoutNode<T> GetNode(T content) {
    102       LayoutNode<T> layoutNode;
    103       nodeMap.TryGetValue(content, out layoutNode);
    104       return layoutNode;
    105     }
    106 
    107     public void ResetCoordinates() {
    108       foreach (var node in nodeMap.Values) {
    109         node.ResetCoordinates();
    110       }
    111     }
    112 
    113     public Dictionary<T, PointF> GetCoordinates() {
    114       return nodeMap.ToDictionary(x => x.Key, x => new PointF(x.Value.X, x.Value.Y));
    115     }
     71        Expand(node, map);
     72      }
     73    }
     74
    11675
    11776    /// <summary>
    11877    /// Transform LayoutNode coordinates so that all coordinates are positive and start from (0,0)
    11978    /// </summary>
    120     private void NormalizeCoordinates() {
    121       var nodes = nodeMap.Values.ToList();
     79    private static void NormalizeCoordinates(IEnumerable<LayoutNode<T>> nodes) {
    12280      float xmin = 0, ymin = 0;
    12381      foreach (var node in nodes) {
     
    13189    }
    13290
    133     public void Center(float width, float height) {
     91    private void Center(float width, float height, IEnumerable<LayoutNode<T>> nodes) {
    13492      // center layout on screen
    135       var bounds = Bounds();
     93      var bounds = Bounds(nodes);
    13694      float dx = 0, dy = 0;
    13795      if (width > bounds.Width) { dx = (width - bounds.Width) / 2f; }
    13896      if (height > bounds.Height) { dy = (height - bounds.Height) / 2f; }
    139       foreach (var node in nodeMap.Values) { node.Translate(dx, dy); }
    140     }
    141 
    142     public void FitToBounds(float width, float height) {
    143       var bounds = Bounds();
     97      foreach (var node in nodes) { node.Translate(dx, dy); }
     98    }
     99
     100    private void FitToBounds(float width, float height, IEnumerable<LayoutNode<T>> nodes) {
     101      var bounds = Bounds(nodes);
    144102      var myWidth = bounds.Width;
    145103      var myHeight = bounds.Height;
     
    147105      if (myWidth <= width && myHeight <= height) return; // no need to fit since we are within bounds
    148106
    149       var layers = nodeMap.Values.GroupBy(node => node.Level, node => node).ToList();
     107      var layers = nodes.GroupBy(node => node.Level, node => node).ToList();
    150108
    151109      if (myWidth > width) {
     
    158116        float spacing = minHorizontalSpacing * x;
    159117        foreach (var layer in layers) {
    160           var nodes = layer.ToList();
     118          var nodesLayer = layer.ToList();
    161119          float minWidth = float.MaxValue;
    162           for (int i = 0; i < nodes.Count - 1; ++i) { minWidth = Math.Min(minWidth, nodes[i + 1].X - nodes[i].X); }
     120          for (int i = 0; i < nodesLayer.Count - 1; ++i) { minWidth = Math.Min(minWidth, nodesLayer[i + 1].X - nodesLayer[i].X); }
    163121          float w = Math.Min(NodeWidth, minWidth - spacing);
    164           foreach (var node in nodes) {
     122          foreach (var node in nodesLayer) {
    165123            node.X += (node.Width - w) / 2f;
    166124            node.Width = w;
     
    186144    }
    187145
    188     public void Clear() {
    189       layoutRoot = null;
    190       nodeMap.Clear();
    191     }
    192 
    193     public void Reset() {
    194       foreach (var layoutNode in nodeMap.Values) {
    195         // reset layout-related parameters
    196         layoutNode.Reset();
    197         // reset the width and height since they might have been affected by scaling
    198         layoutNode.Width = NodeWidth;
    199         layoutNode.Height = NodeHeight;
    200       }
    201     }
    202 
    203     public void CalculateLayout() {
    204       if (layoutRoot == null) throw new Exception("Layout layoutRoot cannot be null.");
    205       Reset(); // reset node parameters like Mod, Shift etc. and set coordinates to 0
    206       FirstWalk(layoutRoot);
    207       SecondWalk(layoutRoot, -layoutRoot.Prelim);
    208       NormalizeCoordinates();
    209     }
    210 
    211     public void CalculateLayout(float width, float height) {
    212       CalculateLayout();
    213       FitToBounds(width, height);
    214       Center(width, height);
    215     }
    216146
    217147    /// <summary>
     
    219149    /// </summary>
    220150    /// <returns></returns>
    221     public RectangleF Bounds() {
     151    private RectangleF Bounds(IEnumerable<LayoutNode<T>> nodes) {
    222152      float xmin = 0, xmax = 0, ymin = 0, ymax = 0;
    223       var list = nodeMap.Values.ToList();
    224       foreach (LayoutNode<T> node in list) {
     153      foreach (LayoutNode<T> node in nodes) {
    225154        float x = node.X, y = node.Y;
    226155        if (xmin > x) xmin = x;
Note: See TracChangeset for help on using the changeset viewer.