#region License Information /* HeuristicLab * Copyright (C) 2002-2014 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.Drawing; using System.Linq; using HeuristicLab.Core.Views; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views; using HeuristicLab.EvolutionTracking; using HeuristicLab.MainForm; using HeuristicLab.Visualization; namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views { [View("FragmentGraphView")] [Content(typeof(FragmentGraph), IsDefaultView = true)] public sealed partial class FragmentGraphView : ItemView { private const int PreferredHorizontalSpacing = 10; private const int PreferredVerticalSpacing = 25; private ReingoldTilfordLayoutEngine layoutEngine; private ReingoldTilfordLayoutEngine symbolicExpressionEngine; private Dictionary tileDictionary; private SymbolicExpressionTreeTile Root { get; set; } public new FragmentGraph Content { get { return (FragmentGraph)base.Content; } set { base.Content = value; } } public FragmentGraphView() { InitializeComponent(); layoutEngine = new ReingoldTilfordLayoutEngine(n => n.Children) { HorizontalSpacing = PreferredHorizontalSpacing, VerticalSpacing = PreferredVerticalSpacing, }; symbolicExpressionEngine = new ReingoldTilfordLayoutEngine(n => n.Subtrees) { HorizontalSpacing = PreferredHorizontalSpacing, VerticalSpacing = PreferredVerticalSpacing, NodeWidth = 80, NodeHeight = 40 }; tileDictionary = new Dictionary(); } private void MakeTiles() { var chart = symbolicExpressionChartControl.Chart; tileDictionary.Clear(); foreach (var node in Content.Nodes) { var graphNode = (IGenealogyGraphNode)node.Content; var tile = new SymbolicExpressionTreeTile(chart); tile.LayoutEngine = symbolicExpressionEngine; tile.Label = "Generation " + node.Content.Rank + Environment.NewLine + "Quality " + String.Format("{0:0.000}", node.Content.Quality); tile.Root = graphNode.Content.Root; var tileNode = new TileLayoutNode { Tile = tile }; tileDictionary.Add(node, tileNode); } foreach (var node in Content.Nodes.Where(n => n.OutArcs.Any())) { var layoutNode = tileDictionary[node]; layoutNode.Children = new List(node.OutArcs.Select(a => tileDictionary[(FragmentNode)a.Target])); } } private void Draw() { var chart = symbolicExpressionChartControl.Chart; var nodes = Content.Nodes.ToList(); var root = nodes[0]; var fragmentRoot = tileDictionary[root]; int maxTileWidth = 0, maxTileHeight = 0; var tiles = nodes.Select(x => tileDictionary[x].Tile).ToList(); foreach (var tile in tiles) { var size = tile.Size; if (maxTileWidth < size.Width) maxTileWidth = size.Width; if (maxTileHeight < size.Height) maxTileHeight = size.Height; } layoutEngine.NodeWidth = maxTileWidth; layoutEngine.NodeHeight = maxTileHeight; layoutEngine.HorizontalSpacing = PreferredHorizontalSpacing; layoutEngine.VerticalSpacing = PreferredVerticalSpacing; var visualNodes = layoutEngine.CalculateLayout(fragmentRoot); symbolicExpressionChartControl.UpdateEnabled = false; foreach (var visualNode in visualNodes) { var tile = visualNode.Content.Tile; tile.Position = new Point(visualNode.X, visualNode.Y); symbolicExpressionChartControl.Add(tile); } // add connections between the tiles foreach (var node in nodes) { var aTile = tileDictionary[node].Tile; var aSize = aTile.Size; var aPos = aTile.Position; var graphNode = node.Content; if (node.SubtreeIndex > 0) { var subtree = graphNode.Content.Root.NodeAt(node.SubtreeIndex); foreach (var s in subtree.IterateNodesPrefix()) { var primitive = aTile.GetPrimitive(s); if (primitive != null) { var rpb = primitive as RectangularPrimitiveBase; if (rpb != null) { rpb.Pen = new Pen(Color.Black); } } } } if (node.FragmentIndex > 0) { var subtree = graphNode.Content.Root.NodeAt(node.FragmentIndex); foreach (var s in subtree.IterateNodesPrefix()) { var primitive = aTile.GetPrimitive(s); if (primitive != null) { var rpb = primitive as RectangularPrimitiveBase; if (rpb != null) { rpb.Brush = new SolidBrush(Color.LightGray); } } } } if (node.InArcs.Any()) { var parent = (FragmentNode)node.InArcs.First().Source; if (parent.OutArcs.First().Target == node) { var index = node.SubtreeIndex + (parent.FragmentIndex - parent.SubtreeIndex); // some mutations create discontinuities which invalidate the index if (index >= 0 && index < graphNode.Content.Length) { var subtree = graphNode.Content.NodeAt(index); var primitive = aTile.GetPrimitive(subtree); primitive.Brush = new SolidBrush(Color.LightCoral); } } } foreach (var child in node.OutArcs.Select(x => (FragmentNode)x.Target)) { var bTile = tileDictionary[child].Tile; var bSize = bTile.Size; var bPos = bTile.Position; var line = new Line(chart, new PointD(aPos.X + aSize.Width / 2.0, aPos.Y + aSize.Height), new PointD(bPos.X + bSize.Width / 2.0, bPos.Y)) { Pen = Pens.DimGray }; symbolicExpressionChartControl.Add(line); } } // center display on the root of the fragment graph symbolicExpressionChartControl.Chart.Move(tileDictionary[root].Tile.Position.X, tileDictionary[root].Tile.Position.Y); symbolicExpressionChartControl.UpdateEnabled = true; symbolicExpressionChartControl.EnforceUpdate(); } protected override void DeregisterContentEvents() { // TODO: Deregister your event handlers here base.DeregisterContentEvents(); } protected override void RegisterContentEvents() { base.RegisterContentEvents(); // TODO: Register your event handlers here } #region Event Handlers (Content) // TODO: Put event handlers of the content here protected override void OnContentChanged() { base.OnContentChanged(); if (Content != null) { MakeTiles(); Draw(); } } #endregion protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); // TODO: Enable or disable controls based on whether the content is null or the view is set readonly } #region Event Handlers (child controls) // TODO: Put event handlers of child controls here. #endregion } internal static class Util { internal static ISymbolicExpressionTreeNode NodeAt(this ISymbolicExpressionTree tree, int position) { return NodeAt(tree.Root, position); } internal static ISymbolicExpressionTreeNode NodeAt(this ISymbolicExpressionTreeNode root, int position) { return root.IterateNodesPrefix().ElementAt(position); } } internal class TileLayoutNode { public SymbolicExpressionTreeTile Tile { get; set; } private List children; public IEnumerable Children { get { return children ?? Enumerable.Empty(); } set { children = value.ToList(); } } } }