#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 System.Windows.Forms;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
using HeuristicLab.EvolutionTracking;
using HeuristicLab.EvolutionTracking.Views;
using HeuristicLab.MainForm;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views {
[View("SymbolicDataAnalysisGenealogyGraphView")]
[Content(typeof(IGenealogyGraph), IsDefaultView = true)]
public partial class SymbolicDataAnalysisGenealogyGraphView : SymbolicDataAnalysisGenealogyGraphViewDesignable {
private readonly ISymbolicExpressionTreeNodeEqualityComparer comparer;
private ISymbolicExpressionTreeNode selectedSubtree;
private ISymbolicExpressionTreeNode SelectedSubtree {
get {
return selectedSubtree;
}
set {
if (selectedSubtree == value) return;
selectedSubtree = value;
ClonedSubtree = (ISymbolicExpressionTreeNode)selectedSubtree.Clone();
}
}
private ISymbolicExpressionTreeNode ClonedSubtree { get; set; }
public SymbolicDataAnalysisGenealogyGraphView() {
InitializeComponent();
comparer = new SymbolicExpressionTreeNodeEqualityComparer();
viewHost.ViewType = typeof(GraphicalSymbolicExpressionTreeView);
}
#region event handlers
protected override void OnContentChanged() {
base.OnContentChanged();
if (Content != null) { genealogyGraphChart.GenealogyGraph = Content; }
}
#endregion
public SymbolicExpressionTreeChart SymbolicExpressionTreeChart {
get {
var view = (GraphicalSymbolicExpressionTreeView)base.viewHost.ActiveView;
return view == null ? null : view.SymbolicExpressionTreeChart;
}
}
protected override void RegisterContentEvents() {
base.RegisterContentEvents();
if (SymbolicExpressionTreeChart != null)
SymbolicExpressionTreeChart.SymbolicExpressionTreeNodeClicked += treeChart_SymbolicExpressionTreeNodeClicked;
}
protected override void DeregisterContentEvents() {
if (SymbolicExpressionTreeChart != null)
SymbolicExpressionTreeChart.SymbolicExpressionTreeNodeClicked -= treeChart_SymbolicExpressionTreeNodeClicked;
base.DeregisterContentEvents();
}
public override void graphChart_GenealogyGraphNodeClicked(object sender, MouseEventArgs args) {
base.graphChart_GenealogyGraphNodeClicked(sender, args);
var visualNode = (VisualGenealogyGraphNode)sender;
var graphNode = (IGenealogyGraphNode)visualNode.Data;
nodeQualityLabel.Text = String.Format("{0:0.000}", graphNode.Quality);
nodeRankLabel.Text = String.Format("{0:0.0}", graphNode.Rank);
nodeDegreeLabel.Text = String.Format("{0} / {1}", graphNode.InDegree, graphNode.OutDegree);
nodeWeightLabel.Text = String.Format("{0:0.00}", graphNode.Weight);
if (openNew_CheckBox.Checked) {
// get the ancestors into a new view
var cloner = new Cloner();
// clone arcs and vertices and use them to create a new graph
// that will include just the individual and its ancestors
var graph = new GenealogyGraph();
var ancestors = new[] { graphNode }.Concat(graphNode.Ancestors).ToList();
graph.AddVertices(ancestors.Select(cloner.Clone));
graph.AddArcs(ancestors.SelectMany(x => x.InArcs).Select(cloner.Clone));
MainFormManager.MainForm.ShowContent(graph);
}
if (graphNode.InArcs.Any()) {
var data = graphNode.InArcs.Last().Data;
var fragment = data as IFragment;
var td = data as TraceData;
if (fragment != null) {
treeChart_HighlightSubtree(graphNode.Data.NodeAt(fragment.Index1));
} else if (td != null) {
var nodes = graphNode.Data.IterateNodesPrefix().ToList();
treeChart_HighlightSubtree(nodes[td.LastSubtreeIndex], Color.Orange);
treeChart_HighlightSubtree(nodes[td.LastFragmentIndex], null, Color.PowderBlue);
}
}
}
public void treeChart_SymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs args) {
var visualNode = (VisualTreeNode)sender;
var subtree = visualNode.Content;
// highlight the selected subtree inside the displayed tree on the right hand side
treeChart_ClearColors();
treeChart_HighlightSubtree(subtree);
bool trace = genealogyGraphChart.TraceFragments; // check whether we are in 'trace' or 'match' mode
switch (args.Button) {
case MouseButtons.Left:
SelectedSubtree = subtree;
if (trace) {
// perform fragment tracing
var graphNode = (IGenealogyGraphNode)genealogyGraphChart.SelectedGraphNode;
var subtreeIndex = graphNode.Data.IterateNodesPrefix().ToList().IndexOf(subtree);
var traceGraph = TraceCalculator.TraceSubtree(graphNode, subtreeIndex);
if (traceGraph.Vertices.Any()) {
genealogyGraphChart.UpdateEnabled = false;
genealogyGraphChart.ClearArcs();
var genealogyNodes = traceGraph.Vertices.Select(v => Content.GetByContent(v.Data));
genealogyGraphChart.HighlightNodes(genealogyNodes, Color.Black, false);
genealogyGraphChart.UpdateEnabled = true;
genealogyGraphChart.EnforceUpdate();
if (openNew_CheckBox.Checked)
MainFormManager.MainForm.ShowContent(traceGraph); // display the fragment graph on the screen
var max = traceGraph.Vertices.Max(x => x.Weight);
genealogyGraphChart.UpdateEnabled = false;
foreach (var traceNode in traceGraph.Vertices) {
var g = Content.GetByContent(traceNode.Data);
g.Weight = traceNode.Weight;
var v = genealogyGraphChart.GetMappedNode(g);
if (v != null) {
int i = (int)Math.Round(g.Weight * (ColorGradient.Colors.Count - 1) / max);
v.Brush = new SolidBrush(ColorGradient.Colors[i]);
}
}
genealogyGraphChart.UpdateEnabled = true;
genealogyGraphChart.EnforceUpdate();
}
} else {
// perform matching like it was done before
// currently there is no possibility to specify the subtree matching criteria
var trees = Content.Vertices.Select(x => x.Data);
var matchingTrees = trees.Where(x => x.Root.ContainsSubtree(subtree, comparer));
var matchingVertices = matchingTrees.Select(x => Content.GetByContent(x));
graphChart_HighlightMatchingVertices(matchingVertices);
}
break;
case MouseButtons.Middle:
var index = SelectedSubtree.IterateNodesPrefix().ToList().IndexOf(subtree);
if (index > 0) {
var s = ClonedSubtree.NodeAt(index);
s.Parent.RemoveSubtree(s.Parent.IndexOfSubtree(s));
var trees = Content.Vertices.Select(x => x.Data);
var matchingTrees = trees.Where(x => x.Root.ContainsSubtree(ClonedSubtree, comparer));
var matchingVertices = matchingTrees.Select(x => Content.GetByContent(x));
treeChart_HighlightSubtree(subtree, Color.Black, Color.White);
graphChart_HighlightMatchingVertices(matchingVertices);
}
break;
}
}
private void graphChart_HighlightMatchingVertices(IEnumerable vertices) {
genealogyGraphChart.Chart.UpdateEnabled = false;
genealogyGraphChart.ClearPrimitives();
genealogyGraphChart.HighlightNodes(vertices);
genealogyGraphChart.Chart.UpdateEnabled = true;
genealogyGraphChart.Chart.EnforceUpdate();
}
private void treeChart_ClearColors() {
foreach (var node in SymbolicExpressionTreeChart.Tree.IterateNodesPrefix()) {
var visualNode = SymbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(node);
if (visualNode != null) {
visualNode.LineColor = Color.Black;
visualNode.FillColor = Color.Transparent;
}
}
SymbolicExpressionTreeChart.RepaintNodes();
}
private void treeChart_HighlightSubtree(ISymbolicExpressionTreeNode subtree, Color? lineColor = null, Color? fillColor = null) {
var lc = lineColor ?? Color.RoyalBlue;
var fc = fillColor ?? Color.White;
foreach (var s in subtree.IterateNodesPrefix()) {
var visualNode = SymbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(s);
visualNode.LineColor = lc;
visualNode.FillColor = fc;
foreach (var c in s.Subtrees) {
var visualArc = SymbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNodeConnection(s, c);
visualArc.LineColor = lc;
}
}
SymbolicExpressionTreeChart.RepaintNodes();
}
private void navigateLeftButton_Click(object sender, EventArgs e) {
var graphNode = (IGenealogyGraphNode)genealogyGraphChart.SelectedGraphNode;
var inArcs = (List)((IVertex)graphNode).InArcs;
if (inArcs.Count > 0) {
genealogyGraphChart.SelectedGraphNode = (IGenealogyGraphNode)inArcs[0].Source;
}
}
private void navigateRightButton_Click(object sender, EventArgs e) {
var graphNode = (IGenealogyGraphNode)genealogyGraphChart.SelectedGraphNode;
var inArcs = graphNode.InArcs.ToList();
if (inArcs.Count > 0) {
var data = inArcs.Last().Data;
var fragment = (data as IFragment);
var td = data as TraceData;
if (fragment != null) {
genealogyGraphChart.SelectedGraphNode = (IGenealogyGraphNode)inArcs.Last().Source;
} else if (td != null) {
if (inArcs.Count == 1) {
genealogyGraphChart.SelectedGraphNode = (IGenealogyGraphNode)inArcs[0].Source;
return;
}
// the first arc from inArcs will always represent the connection to the root parent
// (tracing the body of the traced subtree)
// therefore, in order to follow the correct non-root parent, we have to find the correct arc
// But, the FragmentIndex recorded in the TraceData of the first arc has to match the SubtreeIndex recorded in the TraceData of the searched arc
td = (TraceData)inArcs[0].Data;
var f1 = (graphNode.Data).NodeAt(td.LastFragmentIndex);
for (int i = 1; i < inArcs.Count; ++i) {
td = (TraceData)inArcs[i].Data;
var f2 = ((IGenealogyGraphNode)inArcs[i].Source).Data.NodeAt(td.SubtreeIndex);
// if the subtrees are the same we have found a match
// note: this is not necessarily 100% correct if in the trace graph we have identical fragments on different arcs
if (f1.Difference(f2) == null) {
genealogyGraphChart.SelectedGraphNode = (IGenealogyGraphNode)inArcs[i].Source;
return;
}
}
throw new InvalidOperationException("Error calculating the next step.");
}
}
}
}
public class SymbolicDataAnalysisGenealogyGraphViewDesignable : GenealogyGraphView { }
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);
}
}
}