Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
02/21/14 12:52:09 (11 years ago)
Author:
bburlacu
Message:

#2076: Integrated the Reingold-Tilford layout into the trunk.

Location:
trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views
Files:
2 edited

Legend:

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

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

    r9587 r10496  
    2424using System.Drawing;
    2525using System.Drawing.Imaging;
     26using System.IO;
     27using System.Linq;
    2628using System.Windows.Forms;
     29using Point = System.Drawing.Point;
    2730
    2831namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
    2932  public partial class SymbolicExpressionTreeChart : UserControl {
    3033    private Image image;
    31     private StringFormat stringFormat;
     34    private readonly StringFormat stringFormat;
    3235    private Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode> visualTreeNodes;
    3336    private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection> visualLines;
     37    private readonly ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode> layoutEngine;
     38    private readonly SymbolicExpressionTreeLayoutAdapter layoutAdapter;
     39
     40    private const int preferredNodeWidth = 70;
     41    private const int preferredNodeHeight = 46;
     42    private const int minHorizontalDistance = 20;
     43    private const int minVerticalDistance = 20;
     44
    3445
    3546    public SymbolicExpressionTreeChart() {
     
    4051      this.lineColor = Color.Black;
    4152      this.backgroundColor = Color.White;
    42       this.textFont = new Font("Times New Roman", 8);
     53      this.textFont = new Font(FontFamily.GenericSansSerif, 12);
     54      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>();
     55      layoutAdapter = new SymbolicExpressionTreeLayoutAdapter();
    4356    }
    4457
    4558    public SymbolicExpressionTreeChart(ISymbolicExpressionTree tree)
    4659      : this() {
     60      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>();
     61      layoutAdapter = new SymbolicExpressionTreeLayoutAdapter();
    4762      this.Tree = tree;
    4863    }
    4964
     65    #region Public properties
    5066    private int spacing;
    5167    public int Spacing {
     
    106122      set { suspendRepaint = value; }
    107123    }
     124    #endregion
    108125
    109126    protected override void OnPaint(PaintEventArgs e) {
     
    157174        graphics.Clear(backgroundColor);
    158175        if (tree != null) {
    159           int height = this.Height / tree.Depth;
    160           DrawFunctionTree(tree, graphics, 0, 0, this.Width, height);
     176          DrawFunctionTree(tree, graphics, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
    161177        }
    162178      }
     
    250266
    251267    #region methods for painting the symbolic expression tree
    252     private void DrawFunctionTree(ISymbolicExpressionTree tree, Graphics graphics, int x, int y, int width, int height) {
    253       DrawFunctionTree(tree.Root, graphics, x, y, width, height, Point.Empty);
    254     }
    255 
    256     /// <summary>
    257     ///
    258     /// </summary>
    259     /// <param name="functionTree"> function tree to draw</param>
    260     /// <param name="graphics">graphics object to draw on</param>
    261     /// <param name="x">x coordinate of drawing area</param>
    262     /// <param name="y">y coordinate of drawing area</param>
    263     /// <param name="width">width of drawing area</param>
    264     /// <param name="height">height of drawing area</param>
    265     private void DrawFunctionTree(ISymbolicExpressionTreeNode node, Graphics graphics, int x, int y, int width, int height, Point connectionPoint) {
    266       VisualSymbolicExpressionTreeNode visualTreeNode = visualTreeNodes[node];
    267       float center_x = x + width / 2;
    268       float center_y = y + height / 2;
    269       int actualWidth = width - spacing;
    270       int actualHeight = height - spacing;
    271 
    272       using (var textBrush = new SolidBrush(visualTreeNode.TextColor))
    273       using (var nodeLinePen = new Pen(visualTreeNode.LineColor))
    274       using (var nodeFillBrush = new SolidBrush(visualTreeNode.FillColor)) {
    275 
    276         //calculate size of node
    277         if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) {
    278           visualTreeNode.Width = visualTreeNode.PreferredWidth;
    279           visualTreeNode.Height = visualTreeNode.PreferredHeight;
    280           visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2;
    281           visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2;
    282         }
    283           //width too small to draw in desired sized
    284         else if (actualWidth < visualTreeNode.PreferredWidth && actualHeight >= visualTreeNode.PreferredHeight) {
    285           visualTreeNode.Width = actualWidth;
    286           visualTreeNode.Height = visualTreeNode.PreferredHeight;
    287           visualTreeNode.X = x;
    288           visualTreeNode.Y = (int)center_y - visualTreeNode.Height / 2;
    289         }
    290           //height too small to draw in desired sized
    291         else if (actualWidth >= visualTreeNode.PreferredWidth && actualHeight < visualTreeNode.PreferredHeight) {
    292           visualTreeNode.Width = visualTreeNode.PreferredWidth;
    293           visualTreeNode.Height = actualHeight;
    294           visualTreeNode.X = (int)center_x - visualTreeNode.Width / 2;
    295           visualTreeNode.Y = y;
    296         }
    297           //width and height too small to draw in desired size
    298         else {
    299           visualTreeNode.Width = actualWidth;
    300           visualTreeNode.Height = actualHeight;
    301           visualTreeNode.X = x;
    302           visualTreeNode.Y = y;
    303         }
    304 
    305         //draw terminal node
    306         if (node.SubtreeCount == 0) {
    307           graphics.FillRectangle(nodeFillBrush, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
    308           graphics.DrawRectangle(nodeLinePen, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
    309         } else {
    310           graphics.FillEllipse(nodeFillBrush, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
    311           graphics.DrawEllipse(nodeLinePen, visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height);
    312         }
    313 
    314         //draw name of symbol
    315         var text = node.ToString();
    316         graphics.DrawString(text, textFont, textBrush, new RectangleF(visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width, visualTreeNode.Height), stringFormat);
    317 
    318         //draw connection line to parent node
    319         if (!connectionPoint.IsEmpty && node.Parent != null) {
    320           var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node.Parent, node);
    321           using (Pen linePen = new Pen(visualLine.LineColor)) {
     268    private void DrawFunctionTree(ISymbolicExpressionTree symbolicExpressionTree, Graphics graphics, int preferredWidth, int preferredHeight, int minHDistance, int minVDistance) {
     269      var layoutNodes = layoutAdapter.Convert(symbolicExpressionTree).ToList();
     270      layoutEngine.Reset();
     271      layoutEngine.Root = layoutNodes[0];
     272      layoutEngine.AddNodes(layoutNodes);
     273      layoutEngine.MinHorizontalSpacing = (preferredWidth + minHDistance);
     274      layoutEngine.MinVerticalSpacing = (preferredHeight + minVDistance);
     275      layoutEngine.CalculateLayout();
     276      var bounds = layoutEngine.Bounds();
     277
     278      double verticalScalingFactor = 1.0;
     279      double layoutHeight = (bounds.Height + preferredHeight);
     280      if (this.Height < layoutHeight)
     281        verticalScalingFactor = this.Height / layoutHeight;
     282
     283      double horizontalScalingFactor = 1.0;
     284      double layoutWidth = (bounds.Width + preferredWidth);
     285      if (this.Width < layoutWidth)
     286        horizontalScalingFactor = this.Width / layoutWidth;
     287
     288      double horizontalOffset;
     289      if (this.Width > layoutWidth)
     290        horizontalOffset = (this.Width - layoutWidth) / 2.0;
     291      else
     292        horizontalOffset = preferredWidth / 2.0;
     293
     294      var levels = layoutNodes.GroupBy(n => n.Level, n => n).ToList();
     295      for (int i = levels.Count - 1; i >= 0; --i) {
     296        var nodes = levels[i].ToList();
     297
     298        double minSpacing = double.MaxValue;
     299        if (nodes.Count > 1) {
     300          for (int j = 1; j < nodes.Count() - 1; ++j) {
     301            var distance = nodes[j].X - nodes[j - 1].X; // guaranteed to be > 0
     302            if (minSpacing > distance) minSpacing = distance;
     303          }
     304        }
     305        minSpacing *= horizontalScalingFactor;
     306
     307        int minWidth = (int)Math.Round(preferredWidth * horizontalScalingFactor);
     308        int width = (int)Math.Min(minSpacing, preferredWidth) - 2; // leave some pixels so that node margins don't overlap
     309
     310        foreach (var layoutNode in nodes) {
     311          var visualNode = visualTreeNodes[layoutNode.Content];
     312          visualNode.Width = width;
     313          visualNode.Height = (int)Math.Round(preferredHeight * verticalScalingFactor);
     314          visualNode.X = (int)Math.Round((layoutNode.X + horizontalOffset) * horizontalScalingFactor + (minWidth - width) / 2.0);
     315          visualNode.Y = (int)Math.Round(layoutNode.Y * verticalScalingFactor);
     316          DrawTreeNode(graphics, visualNode);
     317        }
     318      }
     319      // draw node connections
     320      foreach (var visualNode in visualTreeNodes.Values) {
     321        var node = visualNode.SymbolicExpressionTreeNode;
     322        foreach (var subtree in node.Subtrees) {
     323          var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node, subtree);
     324          var visualSubtree = visualTreeNodes[subtree];
     325          var origin = new Point(visualNode.X + visualNode.Width / 2, visualNode.Y + visualNode.Height);
     326          var target = new Point(visualSubtree.X + visualSubtree.Width / 2, visualSubtree.Y);
     327          graphics.Clip = new Region(new Rectangle(Math.Min(origin.X, target.X), origin.Y, Math.Max(origin.X, target.X), target.Y));
     328          using (var linePen = new Pen(visualLine.LineColor)) {
    322329            linePen.DashStyle = visualLine.DashStyle;
    323             graphics.DrawLine(linePen, connectionPoint, new Point(visualTreeNode.X + visualTreeNode.Width / 2, visualTreeNode.Y));
     330            graphics.DrawLine(linePen, origin, target);
    324331          }
    325         }
    326 
    327         //calculate areas for the subtrees according to their tree size and call drawFunctionTree
    328         Point connectFrom = new Point(visualTreeNode.X + visualTreeNode.Width / 2, visualTreeNode.Y + visualTreeNode.Height);
    329         int[] xBoundaries = new int[node.SubtreeCount + 1];
    330         xBoundaries[0] = x;
    331         for (int i = 0; i < node.SubtreeCount; i++) {
    332           xBoundaries[i + 1] = (int)(xBoundaries[i] + (width * (double)node.GetSubtree(i).GetLength()) / (node.GetLength() - 1));
    333           DrawFunctionTree(node.GetSubtree(i), graphics, xBoundaries[i], y + height, xBoundaries[i + 1] - xBoundaries[i], height, connectFrom);
    334332        }
    335333      }
     
    365363    }
    366364    #endregion
    367 
    368365    #region save image
    369366    private void saveImageToolStripMenuItem_Click(object sender, EventArgs e) {
     
    380377      Image image = new Bitmap(Width, Height);
    381378      using (Graphics g = Graphics.FromImage(image)) {
    382         int height = this.Height / tree.Depth;
    383         DrawFunctionTree(tree, g, 0, 0, Width, height);
     379        DrawFunctionTree(tree, g, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
    384380      }
    385381      image.Save(filename);
     
    391387        using (Metafile file = new Metafile(filename, g.GetHdc())) {
    392388          using (Graphics emfFile = Graphics.FromImage(file)) {
    393             int height = this.Height / tree.Depth;
    394             DrawFunctionTree(tree, emfFile, 0, 0, Width, height);
     389            DrawFunctionTree(tree, emfFile, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
    395390          }
    396391        }
    397392        g.ReleaseHdc();
     393      }
     394    }
     395    #endregion
     396    #region export pgf/tikz
     397    private void exportLatexToolStripMenuItem_Click(object sender, EventArgs e) {
     398      using (var dialog = new SaveFileDialog { Filter = "Tex (*.tex)|*.tex" }) {
     399        if (dialog.ShowDialog() != DialogResult.OK) return;
     400        string filename = dialog.FileName.ToLower();
     401        var formatter = new SymbolicExpressionTreeLatexFormatter();
     402        File.WriteAllText(filename, formatter.Format(Tree));
    398403      }
    399404    }
Note: See TracChangeset for help on using the changeset viewer.