#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); } } }