#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(IGenealogyGraph>), 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>, TileLayoutNode> tileDictionary;
private SymbolicExpressionTreeTile Root { get; set; }
public new IGenealogyGraph> Content {
get { return (IGenealogyGraph>)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>, TileLayoutNode>();
}
private void MakeTiles() {
var chart = symbolicExpressionChartControl.Chart;
tileDictionary.Clear();
foreach (var node in Content.Nodes) {
var tile = new SymbolicExpressionTreeTile(chart);
tile.LayoutEngine = symbolicExpressionEngine;
tile.Label = "Generation " + node.Rank + Environment.NewLine +
"Quality " + String.Format("{0:0.000}", node.Quality);
tile.Root = node.Content.Root;
var tileNode = new TileLayoutNode { Tile = tile };
tileDictionary.Add(node, tileNode);
}
foreach (var node in Content.Nodes.Where(n => n.Children.Any())) {
var layoutNode = tileDictionary[node];
layoutNode.Children = new List(node.Children.Select(x => tileDictionary[x]));
}
}
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 fragment = node.Content;
if (fragment.Index1 > 0) {
var subtree = fragment.Root.NodeAt(fragment.Index1);
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 (fragment.Index2 > 0) {
var subtree = fragment.Root.NodeAt(fragment.Index2);
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.Parents.Any() && node == node.Parents.First().Children.First()) {
var parent = node.Parents.First();
var index = fragment.Index1 + (parent.Content.Index2 - parent.Content.Index1);
// some mutations create discontinuities which invalidate the index
if (index >= 0 && index < fragment.Root.GetLength()) {
var subtree = fragment.Root.NodeAt(index);
var primitive = aTile.GetPrimitive(subtree);
primitive.Brush = new SolidBrush(Color.LightCoral);
}
}
foreach (var child in node.Children) {
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(); }
}
}
}