source: trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/LayoutEngines/BoxesLayoutEngine.cs @ 10520

Last change on this file since 10520 was 10520, checked in by bburlacu, 5 years ago

#2076: Got rid of layout adapters. Extracted the previous drawing code and made it into another layout engine called the BoxesLayoutEngine (because it divides the areas necessary for each subtree into boxes and recursively applies the layout). Simplified usage of layout engine so that most of the things are handled internally, and the user just has to provide some lambdas telling the engine how to navigate the original tree. Added context option in the SymbolicExpressionTreeChart to choose which layout engine to use for tree drawing. Moved the SymbolicExpressionTreeLatexFormatter to the HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views assembly because it depends on the layout engine.

File size: 5.2 KB
Line 
1
2using System;
3using System.Collections.Generic;
4using System.Drawing;
5using System.Linq;
6
7namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
8  public class BoxesLayoutEngine<T> : ILayoutEngine<T> where T : class {
9    private readonly Dictionary<T, VisualTreeNode<T>> nodeMap;
10
11    public int NodeWidth { get; set; }
12    public int NodeHeight { get; set; }
13    public int HorizontalSpacing { get; set; }
14    public int VerticalSpacing { get; set; }
15    private VisualTreeNode<T> layoutRoot;
16
17    public int Width { get; private set; }
18    public int Height { get; private set; }
19
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; }
23
24    public BoxesLayoutEngine() {
25      nodeMap = new Dictionary<T, VisualTreeNode<T>>();
26    }
27
28    public void CalculateLayout() {
29      throw new Exception("The BoxesLayoutEngine does not support arbitrary bounds. Please use method CalculateLayout(Width, Height)");
30    }
31
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) {
51      var node = new VisualTreeNode<T>(root) {
52        PreferredWidth = NodeWidth,
53        PreferredHeight = NodeHeight
54      };
55      nodeMap.Add(root, node);
56      var children = GetChildren(root).ToList();
57      if (children.Any()) {
58        foreach (var child in children) {
59          Expand(child);
60        }
61      }
62    }
63
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) {
89      float center_x = x + width / 2;
90      float center_y = y + height / 2;
91      int actualWidth = width - VerticalSpacing;
92      int actualHeight = height - VerticalSpacing;
93
94      //calculate size of node
95      if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) {
96        visualTreeNode.Width = visualTreeNode.PreferredWidth;
97        visualTreeNode.Height = visualTreeNode.PreferredHeight;
98        visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2;
99        visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2;
100      }
101        //width too small to draw in desired sized
102      else if (actualWidth < visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) {
103        visualTreeNode.Width = actualWidth;
104        visualTreeNode.Height = visualTreeNode.PreferredHeight;
105        visualTreeNode.X = x;
106        visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2;
107      }
108        //height too small to draw in desired sized
109      else if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight < visualTreeNode.PreferredHeight) {
110        visualTreeNode.Width = visualTreeNode.PreferredWidth;
111        visualTreeNode.Height = actualHeight;
112        visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2;
113        visualTreeNode.Y = y;
114      }
115        //width and height too small to draw in desired size
116      else {
117        visualTreeNode.Width = actualWidth;
118        visualTreeNode.Height = actualHeight;
119        visualTreeNode.X = x;
120        visualTreeNode.Y = y;
121      }
122      //calculate areas for the subtrees according to their tree size and call drawFunctionTree
123      var node = visualTreeNode.Content;
124      var children = GetChildren(node).ToList();
125      int[] xBoundaries = new int[children.Count + 1];
126      xBoundaries[0] = x;
127      for (int i = 0; i < children.Count; i++) {
128        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);
130      }
131    }
132
133    public IEnumerable<VisualTreeNode<T>> GetVisualNodes() {
134      return nodeMap.Values;
135    }
136  }
137}
Note: See TracBrowser for help on using the repository browser.