Ignore:
Timestamp:
02/28/14 11:56:15 (8 years ago)
Author:
bburlacu
Message:

#2076: Got rid of layout adapters. Extracted the previous drawing code and made it into another layout engine called the BoxesLayoutEngine (because it divides the areas necessary for each subtree into boxes and recursively applies the layout). Simplified usage of layout engine so that most of the things are handled internally, and the user just has to provide some lambdas telling the engine how to navigate the original tree. Added context option in the SymbolicExpressionTreeChart to choose which layout engine to use for tree drawing. Moved the SymbolicExpressionTreeLatexFormatter to the HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views assembly because it depends on the layout engine.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs

    r10499 r10520  
    3333    private Image image;
    3434    private readonly StringFormat stringFormat;
    35     private Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode> visualTreeNodes;
    36     private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection> visualLines;
    37     private readonly ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> layoutEngine;
    38     private readonly SymbolicExpressionTreeLayoutAdapter layoutAdapter;
     35    private Dictionary<ISymbolicExpressionTreeNode, VisualTreeNode<ISymbolicExpressionTreeNode>> visualTreeNodes;
     36    private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualTreeNodeConnection> visualLines;
     37    private ILayoutEngine<ISymbolicExpressionTreeNode> layoutEngine;
    3938
    4039    private const int preferredNodeWidth = 70;
    4140    private const int preferredNodeHeight = 46;
    42     private const int minHorizontalDistance = 20;
    43     private const int minVerticalDistance = 20;
    44 
     41    private const int minHorizontalDistance = 30;
     42    private const int minVerticalDistance = 30;
    4543
    4644    public SymbolicExpressionTreeChart() {
     
    5250      this.backgroundColor = Color.White;
    5351      this.textFont = new Font(FontFamily.GenericSansSerif, 12);
    54       layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>();
    55       layoutAdapter = new SymbolicExpressionTreeLayoutAdapter();
     52      //      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> {
     53      layoutEngine = new BoxesLayoutEngine<ISymbolicExpressionTreeNode> {
     54        NodeWidth = preferredNodeWidth,
     55        NodeHeight = preferredNodeHeight,
     56        HorizontalSpacing = minHorizontalDistance,
     57        VerticalSpacing = minVerticalDistance
     58      };
    5659    }
    5760
    5861    public SymbolicExpressionTreeChart(ISymbolicExpressionTree tree)
    5962      : this() {
    60       layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>();
    61       layoutAdapter = new SymbolicExpressionTreeLayoutAdapter();
    6263      this.Tree = tree;
    6364    }
     
    105106      set {
    106107        tree = value;
    107         visualTreeNodes = new Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode>();
    108         visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection>();
    109108        if (tree != null) {
    110           IEnumerable<ISymbolicExpressionTreeNode> nodes;
    111           if (tree.Root.SubtreeCount == 1) nodes = tree.Root.GetSubtree(0).IterateNodesPrefix();
    112           else nodes = tree.Root.IterateNodesPrefix();
    113           foreach (ISymbolicExpressionTreeNode node in nodes) {
    114             visualTreeNodes[node] = new VisualSymbolicExpressionTreeNode(node);
    115             if (node.Parent != null) visualLines[Tuple.Create(node.Parent, node)] = new VisualSymbolicExpressionTreeNodeConnection();
    116           }
    117         }
    118         Repaint();
     109          Repaint();
     110        }
    119111      }
    120112    }
     
    160152    }
    161153
    162     public void RepaintNode(VisualSymbolicExpressionTreeNode visualNode) {
     154    public void RepaintNode(VisualTreeNode<ISymbolicExpressionTreeNode> visualNode) {
    163155      if (!suspendRepaint) {
    164156        using (var graphics = Graphics.FromImage(image)) {
     
    182174    }
    183175
    184     public VisualSymbolicExpressionTreeNode GetVisualSymbolicExpressionTreeNode(ISymbolicExpressionTreeNode symbolicExpressionTreeNode) {
     176    public VisualTreeNode<ISymbolicExpressionTreeNode> GetVisualSymbolicExpressionTreeNode(ISymbolicExpressionTreeNode symbolicExpressionTreeNode) {
    185177      if (visualTreeNodes.ContainsKey(symbolicExpressionTreeNode))
    186178        return visualTreeNodes[symbolicExpressionTreeNode];
     
    188180    }
    189181
    190     public VisualSymbolicExpressionTreeNodeConnection GetVisualSymbolicExpressionTreeNodeConnection(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
     182    public VisualTreeNodeConnection GetVisualSymbolicExpressionTreeNodeConnection(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
    191183      if (child.Parent != parent) throw new ArgumentException();
    192184      var key = Tuple.Create(parent, child);
    193       VisualSymbolicExpressionTreeNodeConnection connection = null;
     185      VisualTreeNodeConnection connection = null;
    194186      visualLines.TryGetValue(key, out connection);
    195187      return connection;
     
    205197
    206198    protected virtual void SymbolicExpressionTreeChart_MouseClick(object sender, MouseEventArgs e) {
    207       VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
     199      var visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
    208200      if (visualTreeNode != null) {
    209201        OnSymbolicExpressionTreeNodeClicked(visualTreeNode, e);
     
    219211
    220212    protected virtual void SymbolicExpressionTreeChart_MouseDoubleClick(object sender, MouseEventArgs e) {
    221       VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
     213      VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
    222214      if (visualTreeNode != null)
    223215        OnSymbolicExpressionTreeNodeDoubleClicked(visualTreeNode, e);
     
    231223    }
    232224
    233     private VisualSymbolicExpressionTreeNode draggedSymbolicExpressionTree;
     225    private VisualTreeNode<ISymbolicExpressionTreeNode> draggedSymbolicExpressionTree;
    234226    private MouseButtons dragButtons;
    235227    private void SymbolicExpressionTreeChart_MouseDown(object sender, MouseEventArgs e) {
     
    243235
    244236    private void SymbolicExpressionTreeChart_MouseMove(object sender, MouseEventArgs e) {
    245       VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
     237      VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
    246238      if (draggedSymbolicExpressionTree != null &&
    247239        draggedSymbolicExpressionTree != visualTreeNode) {
     
    258250    }
    259251
    260     public VisualSymbolicExpressionTreeNode FindVisualSymbolicExpressionTreeNodeAt(int x, int y) {
     252    public VisualTreeNode<ISymbolicExpressionTreeNode> FindVisualSymbolicExpressionTreeNodeAt(int x, int y) {
    261253      foreach (var visualTreeNode in visualTreeNodes.Values) {
    262254        if (x >= visualTreeNode.X && x <= visualTreeNode.X + visualTreeNode.Width &&
     
    269261
    270262    #region methods for painting the symbolic expression tree
    271     private void DrawFunctionTree(ISymbolicExpressionTree symbolicExpressionTree, Graphics graphics, int preferredWidth, int preferredHeight, int minHDistance, int minVDistance) {     
    272       var layoutNodes = layoutAdapter.Convert(symbolicExpressionTree).ToList();
    273       if(symbolicExpressionTree.Root.SubtreeCount==1) layoutNodes.RemoveAt(0);
    274       layoutEngine.Reset();
    275       layoutEngine.Root = layoutNodes[0];
    276       layoutEngine.AddNodes(layoutNodes);
    277       layoutEngine.MinHorizontalSpacing = (preferredWidth + minHDistance);
    278       layoutEngine.MinVerticalSpacing = (preferredHeight + minVDistance);
    279       layoutEngine.CalculateLayout();
    280       var bounds = layoutEngine.Bounds();
    281 
    282       double verticalScalingFactor = 1.0;
    283       double layoutHeight = (bounds.Height + preferredHeight);
    284       if (this.Height < layoutHeight)
    285         verticalScalingFactor = this.Height / layoutHeight;
    286 
    287       double horizontalScalingFactor = 1.0;
    288       double layoutWidth = (bounds.Width + preferredWidth);
    289       if (this.Width < layoutWidth)
    290         horizontalScalingFactor = this.Width / layoutWidth;
    291 
    292       double horizontalOffset;
    293       if (this.Width > layoutWidth)
    294         horizontalOffset = (this.Width - layoutWidth) / 2.0;
    295       else
    296         horizontalOffset = preferredWidth / 2.0;
    297 
    298       var levels = layoutNodes.GroupBy(n => n.Level, n => n).ToList();
    299       for (int i = levels.Count - 1; i >= 0; --i) {
    300         var nodes = levels[i].ToList();
    301 
    302         double minSpacing = double.MaxValue;
    303         if (nodes.Count > 1) {
    304           for (int j = 1; j < nodes.Count() - 1; ++j) {
    305             var distance = nodes[j].X - nodes[j - 1].X; // guaranteed to be > 0
    306             if (minSpacing > distance) minSpacing = distance;
    307           }
    308         }
    309         minSpacing *= horizontalScalingFactor;
    310 
    311         int minWidth = (int)Math.Round(preferredWidth * horizontalScalingFactor);
    312         int width = (int)Math.Min(minSpacing, preferredWidth) - 2; // leave some pixels so that node margins don't overlap
    313 
    314         foreach (var layoutNode in nodes) {
    315           var visualNode = visualTreeNodes[layoutNode.Content];
    316           visualNode.Width = width;
    317           visualNode.Height = (int)Math.Round(preferredHeight * verticalScalingFactor);
    318           visualNode.X = (int)Math.Round((layoutNode.X + horizontalOffset) * horizontalScalingFactor + (minWidth - width) / 2.0);
    319           visualNode.Y = (int)Math.Round(layoutNode.Y * verticalScalingFactor);
    320           DrawTreeNode(graphics, visualNode);
    321         }
    322       }
    323       // draw node connections
    324       foreach (var visualNode in visualTreeNodes.Values) {
    325         var node = visualNode.SymbolicExpressionTreeNode;
     263    private void DrawFunctionTree(ISymbolicExpressionTree symbExprTree, Graphics graphics, int preferredWidth, int preferredHeight, int minHDistance, int minVDistance) {
     264      var root = symbExprTree.Root;
     265      var actualRoot = root.SubtreeCount == 1 ? root.GetSubtree(0) : root;
     266      layoutEngine.NodeWidth = preferredWidth;
     267      layoutEngine.NodeHeight = preferredHeight;
     268      layoutEngine.HorizontalSpacing = minHDistance;
     269      layoutEngine.VerticalSpacing = minVDistance;
     270      layoutEngine.Initialize(actualRoot, n => n.Subtrees, n => n.GetLength(), n => n.GetDepth());
     271      layoutEngine.CalculateLayout(Width, Height);
     272
     273      var visualNodes = layoutEngine.GetVisualNodes().ToList();
     274      visualTreeNodes = visualNodes.ToDictionary(x => x.Content, x => x);
     275      visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualTreeNodeConnection>();
     276      foreach (var node in visualNodes.Select(n => n.Content)) {
     277        foreach (var subtree in node.Subtrees) {
     278          visualLines.Add(new Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>(node, subtree), new VisualTreeNodeConnection());
     279        }
     280      }
     281      // draw nodes and connections
     282      foreach (var visualNode in visualNodes) {
     283        DrawTreeNode(visualNode);
     284        var node = visualNode.Content;
    326285        foreach (var subtree in node.Subtrees) {
    327286          var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node, subtree);
     
    338297    }
    339298
    340     protected void DrawTreeNode(VisualSymbolicExpressionTreeNode visualTreeNode) {
     299    protected void DrawTreeNode(VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode) {
    341300      using (var graphics = Graphics.FromImage(image)) {
    342301        graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
     
    346305    }
    347306
    348     protected void DrawTreeNode(Graphics graphics, VisualSymbolicExpressionTreeNode visualTreeNode) {
     307    protected void DrawTreeNode(Graphics graphics, VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode) {
    349308      graphics.Clip = new Region(new Rectangle(visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width + 1, visualTreeNode.Height + 1));
    350309      graphics.Clear(backgroundColor);
    351       var node = visualTreeNode.SymbolicExpressionTreeNode;
     310      var node = visualTreeNode.Content;
    352311      using (var textBrush = new SolidBrush(visualTreeNode.TextColor))
    353312      using (var nodeLinePen = new Pen(visualTreeNode.LineColor))
     
    408367    }
    409368    #endregion
     369
     370    private void reingoldTilfordToolStripMenuItem_Click(object sender, EventArgs e) {
     371      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> {
     372        NodeWidth = preferredNodeWidth,
     373        NodeHeight = preferredNodeHeight,
     374        HorizontalSpacing = minHorizontalDistance,
     375        VerticalSpacing = minVerticalDistance
     376      };
     377      Repaint();
     378    }
     379
     380    private void boxesToolStripMenuItem_Click(object sender, EventArgs e) {
     381      layoutEngine = new BoxesLayoutEngine<ISymbolicExpressionTreeNode> {
     382        NodeWidth = preferredNodeWidth,
     383        NodeHeight = preferredNodeHeight,
     384        HorizontalSpacing = minHorizontalDistance,
     385        VerticalSpacing = minVerticalDistance
     386      };
     387      Repaint();
     388    }
    410389  }
    411390}
Note: See TracChangeset for help on using the changeset viewer.