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

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

#2076: Updated the way the layout is used in the SymbolicExpressionTreeChart; updated simplifier view accordingly.

File size: 5.3 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<T> GetContentNodes() {
134      return nodeMap.Keys;
135    }
136
137    public IEnumerable<VisualTreeNode<T>> GetVisualNodes() {
138      return nodeMap.Values;
139    }
140  }
141}
Note: See TracBrowser for help on using the repository browser.