#region License Information /* HeuristicLab * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; namespace HeuristicLab.Visualization { public class BoxesLayoutEngine : ILayoutEngine where T : class { public int NodeWidth { get; set; } public int NodeHeight { get; set; } public int HorizontalSpacing { get; set; } public int VerticalSpacing { get; set; } private readonly Func> GetChildren; private readonly Func GetLength; private readonly Func GetDepth; public BoxesLayoutEngine(Func> GetChildren, Func GetLength, Func GetDepth) { if (GetChildren == null) throw new ArgumentNullException("GetChildren"); if (GetLength == null) throw new ArgumentNullException("GetLength"); if (GetDepth == null) throw new ArgumentNullException("GetDepth"); this.GetChildren = GetChildren; this.GetLength = GetLength; this.GetDepth = GetDepth; } public IEnumerable> CalculateLayout(T root, float width, float height) { var nodeMap = new Dictionary>(); CreateVisualNodes(root, nodeMap); RecursiveLayout(nodeMap, nodeMap[root], 0, 0, (int)Math.Round(width), (int)Math.Round(height) / GetDepth(root)); return nodeMap.Values; } private void CreateVisualNodes(T root, Dictionary> map) { var node = new VisualTreeNode(root) { PreferredWidth = NodeWidth, PreferredHeight = NodeHeight }; map.Add(root, node); var children = GetChildren(root).ToList(); if (children.Any()) { foreach (var child in children) { CreateVisualNodes(child, map); } } } private void RecursiveLayout(Dictionary> nodeMap, VisualTreeNode visualTreeNode, int x, int y, int width, int height) { float center_x = x + width / 2; float center_y = y + height / 2; int actualWidth = width - HorizontalSpacing; int actualHeight = height - VerticalSpacing; //calculate size of node if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) { visualTreeNode.Width = visualTreeNode.PreferredWidth; visualTreeNode.Height = visualTreeNode.PreferredHeight; visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2; visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2; } //width too small to draw in desired sized else if (actualWidth < visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) { visualTreeNode.Width = actualWidth; visualTreeNode.Height = visualTreeNode.PreferredHeight; visualTreeNode.X = x; visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2; } //height too small to draw in desired sized else if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight < visualTreeNode.PreferredHeight) { visualTreeNode.Width = visualTreeNode.PreferredWidth; visualTreeNode.Height = actualHeight; visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2; visualTreeNode.Y = y; } //width and height too small to draw in desired size else { visualTreeNode.Width = actualWidth; visualTreeNode.Height = actualHeight; visualTreeNode.X = x; visualTreeNode.Y = y; } //calculate areas for the subtrees according to their tree size var node = visualTreeNode.Content; var children = GetChildren(node).ToList(); int[] xBoundaries = new int[children.Count + 1]; xBoundaries[0] = x; for (int i = 0; i < children.Count; i++) { xBoundaries[i + 1] = (int)(xBoundaries[i] + (width * (double)GetLength(children[i])) / (GetLength(node) - 1)); RecursiveLayout(nodeMap, nodeMap[children[i]], xBoundaries[i], y + height, xBoundaries[i + 1] - xBoundaries[i], height); } } } }