Ignore:
Timestamp:
07/07/14 16:55:50 (5 years ago)
Author:
gkronber
Message:

#2076,#2159, #2092
multi-ticket merge to stable:

Location:
stable
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • stable

  • stable/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views

  • stable/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs

    r9931 r11120  
    2424using System.Drawing;
    2525using System.Drawing.Imaging;
     26using System.IO;
     27using System.Linq;
    2628using System.Windows.Forms;
     29
    2730
    2831namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
    2932  public partial class SymbolicExpressionTreeChart : UserControl {
    3033    private Image image;
    31     private StringFormat stringFormat;
    32     private Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode> visualTreeNodes;
    33     private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection> visualLines;
     34    private readonly StringFormat stringFormat;
     35    private Dictionary<ISymbolicExpressionTreeNode, VisualTreeNode<ISymbolicExpressionTreeNode>> visualTreeNodes;
     36    private Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualTreeNodeConnection> visualLines;
     37    private ILayoutEngine<ISymbolicExpressionTreeNode> layoutEngine;
     38
     39    private const int preferredNodeWidth = 70;
     40    private const int preferredNodeHeight = 46;
     41    private int minHorizontalDistance = 30;
     42    private int minVerticalDistance = 30;
    3443
    3544    public SymbolicExpressionTreeChart() {
     
    4049      this.lineColor = Color.Black;
    4150      this.backgroundColor = Color.White;
    42       this.textFont = new Font("Times New Roman", 8);
     51      this.textFont = new Font(FontFamily.GenericSansSerif, 12);
     52
     53      visualTreeNodes = new Dictionary<ISymbolicExpressionTreeNode, VisualTreeNode<ISymbolicExpressionTreeNode>>();
     54      visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualTreeNodeConnection>();
     55
     56      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>(n => n.Subtrees) {
     57        NodeWidth = preferredNodeWidth,
     58        NodeHeight = preferredNodeHeight,
     59        HorizontalSpacing = minHorizontalDistance,
     60        VerticalSpacing = minVerticalDistance
     61      };
     62      reingoldTilfordToolStripMenuItem.Checked = true;
    4363    }
    4464
     
    4868    }
    4969
     70    #region Public properties
    5071    private int spacing;
    5172    public int Spacing {
     
    89110      set {
    90111        tree = value;
    91         visualTreeNodes = new Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode>();
    92         visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualSymbolicExpressionTreeNodeConnection>();
    93         if (tree != null) {
    94           foreach (ISymbolicExpressionTreeNode node in tree.IterateNodesPrefix()) {
    95             visualTreeNodes[node] = new VisualSymbolicExpressionTreeNode(node);
    96             if (node.Parent != null) visualLines[Tuple.Create(node.Parent, node)] = new VisualSymbolicExpressionTreeNodeConnection();
    97           }
    98         }
    99112        Repaint();
    100113      }
     
    106119      set { suspendRepaint = value; }
    107120    }
     121    #endregion
    108122
    109123    protected override void OnPaint(PaintEventArgs e) {
     
    115129      if (this.Width <= 1 || this.Height <= 1)
    116130        this.image = new Bitmap(1, 1);
    117       else
     131      else {
    118132        this.image = new Bitmap(Width, Height);
     133      }
    119134      this.Repaint();
     135    }
     136
     137    public event EventHandler Repainted;//expose this event to notify the parent control that the tree was repainted
     138    protected virtual void OnRepaint(object sender, EventArgs e) {
     139      var repainted = Repainted;
     140      if (repainted != null) {
     141        repainted(sender, e);
     142      }
    120143    }
    121144
     
    124147        this.GenerateImage();
    125148        this.Refresh();
     149        OnRepaint(this, EventArgs.Empty);
    126150      }
    127151    }
     
    134158          foreach (var visualNode in visualTreeNodes.Values) {
    135159            DrawTreeNode(graphics, visualNode);
     160            if (visualNode.Content.SubtreeCount > 0) {
     161              foreach (var visualSubtree in visualNode.Content.Subtrees.Select(s => visualTreeNodes[s])) {
     162                DrawLine(graphics, visualNode, visualSubtree);
     163              }
     164            }
    136165          }
    137166        }
     
    140169    }
    141170
    142     public void RepaintNode(VisualSymbolicExpressionTreeNode visualNode) {
     171    public void RepaintNode(VisualTreeNode<ISymbolicExpressionTreeNode> visualNode) {
    143172      if (!suspendRepaint) {
    144173        using (var graphics = Graphics.FromImage(image)) {
     
    157186        graphics.Clear(backgroundColor);
    158187        if (tree != null) {
    159           int height = this.Height / tree.Depth;
    160           DrawFunctionTree(tree, graphics, 0, 0, this.Width, height);
    161         }
    162       }
    163     }
    164 
    165     public VisualSymbolicExpressionTreeNode GetVisualSymbolicExpressionTreeNode(ISymbolicExpressionTreeNode symbolicExpressionTreeNode) {
     188          DrawFunctionTree(graphics, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
     189        }
     190      }
     191    }
     192
     193    public VisualTreeNode<ISymbolicExpressionTreeNode> GetVisualSymbolicExpressionTreeNode(ISymbolicExpressionTreeNode symbolicExpressionTreeNode) {
    166194      if (visualTreeNodes.ContainsKey(symbolicExpressionTreeNode))
    167195        return visualTreeNodes[symbolicExpressionTreeNode];
     
    169197    }
    170198
    171     public VisualSymbolicExpressionTreeNodeConnection GetVisualSymbolicExpressionTreeNodeConnection(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
     199    public VisualTreeNodeConnection GetVisualSymbolicExpressionTreeNodeConnection(ISymbolicExpressionTreeNode parent, ISymbolicExpressionTreeNode child) {
    172200      if (child.Parent != parent) throw new ArgumentException();
    173201      var key = Tuple.Create(parent, child);
    174       VisualSymbolicExpressionTreeNodeConnection connection = null;
     202      VisualTreeNodeConnection connection = null;
    175203      visualLines.TryGetValue(key, out connection);
    176204      return connection;
     
    186214
    187215    protected virtual void SymbolicExpressionTreeChart_MouseClick(object sender, MouseEventArgs e) {
    188       VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
     216      var visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
    189217      if (visualTreeNode != null) {
    190218        OnSymbolicExpressionTreeNodeClicked(visualTreeNode, e);
     
    200228
    201229    protected virtual void SymbolicExpressionTreeChart_MouseDoubleClick(object sender, MouseEventArgs e) {
    202       VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
     230      VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
    203231      if (visualTreeNode != null)
    204232        OnSymbolicExpressionTreeNodeDoubleClicked(visualTreeNode, e);
     
    212240    }
    213241
    214     private VisualSymbolicExpressionTreeNode draggedSymbolicExpressionTree;
     242    private VisualTreeNode<ISymbolicExpressionTreeNode> draggedSymbolicExpressionTree;
    215243    private MouseButtons dragButtons;
    216244    private void SymbolicExpressionTreeChart_MouseDown(object sender, MouseEventArgs e) {
     
    224252
    225253    private void SymbolicExpressionTreeChart_MouseMove(object sender, MouseEventArgs e) {
    226       VisualSymbolicExpressionTreeNode visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
     254      VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
    227255      if (draggedSymbolicExpressionTree != null &&
    228256        draggedSymbolicExpressionTree != visualTreeNode) {
     
    239267    }
    240268
    241     public VisualSymbolicExpressionTreeNode FindVisualSymbolicExpressionTreeNodeAt(int x, int y) {
     269    public VisualTreeNode<ISymbolicExpressionTreeNode> FindVisualSymbolicExpressionTreeNodeAt(int x, int y) {
    242270      foreach (var visualTreeNode in visualTreeNodes.Values) {
    243271        if (x >= visualTreeNode.X && x <= visualTreeNode.X + visualTreeNode.Width &&
     
    249277    #endregion
    250278
     279    private void CalculateLayout(int preferredWidth, int preferredHeight, int minHDistance, int minVDistance) {
     280      layoutEngine.NodeWidth = preferredWidth;
     281      layoutEngine.NodeHeight = preferredHeight;
     282      layoutEngine.HorizontalSpacing = minHDistance;
     283      layoutEngine.VerticalSpacing = minVDistance;
     284
     285      var actualRoot = tree.Root;
     286      if (actualRoot.Symbol is ProgramRootSymbol && actualRoot.SubtreeCount == 1) {
     287        actualRoot = tree.Root.GetSubtree(0);
     288      }
     289
     290      var visualNodes = layoutEngine.CalculateLayout(actualRoot, Width, Height).ToList();
     291      visualTreeNodes = visualNodes.ToDictionary(x => x.Content, x => x);
     292      visualLines = new Dictionary<Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>, VisualTreeNodeConnection>();
     293      foreach (var node in visualNodes.Select(n => n.Content)) {
     294        foreach (var subtree in node.Subtrees) {
     295          visualLines.Add(new Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>(node, subtree), new VisualTreeNodeConnection());
     296        }
     297      }
     298    }
     299
    251300    #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)) {
     301    private void DrawFunctionTree(Graphics graphics, int preferredWidth, int preferredHeight, int minHDistance, int minVDistance) {
     302      CalculateLayout(preferredWidth, preferredHeight, minHDistance, minVDistance);
     303      var visualNodes = visualTreeNodes.Values;
     304      //draw nodes and connections
     305      foreach (var visualNode in visualNodes) {
     306        DrawTreeNode(graphics, visualNode);
     307        var node = visualNode.Content;
     308        foreach (var subtree in node.Subtrees) {
     309          var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node, subtree);
     310          var visualSubtree = visualTreeNodes[subtree];
     311          var origin = new Point(visualNode.X + visualNode.Width / 2, visualNode.Y + visualNode.Height);
     312          var target = new Point(visualSubtree.X + visualSubtree.Width / 2, visualSubtree.Y);
     313          graphics.Clip = new Region(new Rectangle(Math.Min(origin.X, target.X), origin.Y, Math.Max(origin.X, target.X), target.Y));
     314          using (var linePen = new Pen(visualLine.LineColor)) {
    322315            linePen.DashStyle = visualLine.DashStyle;
    323             graphics.DrawLine(linePen, connectionPoint, new Point(visualTreeNode.X + visualTreeNode.Width / 2, visualTreeNode.Y));
     316            graphics.DrawLine(linePen, origin, target);
    324317          }
    325318        }
    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);
    334         }
    335       }
    336     }
    337 
    338     protected void DrawTreeNode(VisualSymbolicExpressionTreeNode visualTreeNode) {
    339       using (var graphics = Graphics.FromImage(image)) {
    340         graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
    341         graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
    342         DrawTreeNode(graphics, visualTreeNode);
    343       }
    344     }
    345 
    346     protected void DrawTreeNode(Graphics graphics, VisualSymbolicExpressionTreeNode visualTreeNode) {
     319      }
     320    }
     321
     322    protected void DrawTreeNode(Graphics graphics, VisualTreeNode<ISymbolicExpressionTreeNode> visualTreeNode) {
    347323      graphics.Clip = new Region(new Rectangle(visualTreeNode.X, visualTreeNode.Y, visualTreeNode.Width + 1, visualTreeNode.Height + 1));
    348324      graphics.Clear(backgroundColor);
    349       var node = visualTreeNode.SymbolicExpressionTreeNode;
     325      var node = visualTreeNode.Content;
    350326      using (var textBrush = new SolidBrush(visualTreeNode.TextColor))
    351327      using (var nodeLinePen = new Pen(visualTreeNode.LineColor))
     
    364340      }
    365341    }
     342
     343    protected void DrawLine(Graphics graphics, VisualTreeNode<ISymbolicExpressionTreeNode> startNode, VisualTreeNode<ISymbolicExpressionTreeNode> endNode) {
     344      var origin = new Point(startNode.X + startNode.Width / 2, startNode.Y + startNode.Height);
     345      var target = new Point(endNode.X + endNode.Width / 2, endNode.Y);
     346      graphics.Clip = new Region(new Rectangle(Math.Min(origin.X, target.X), origin.Y, Math.Max(origin.X, target.X), target.Y));
     347      var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(startNode.Content, endNode.Content);
     348      using (var linePen = new Pen(visualLine.LineColor)) {
     349        linePen.DashStyle = visualLine.DashStyle;
     350        graphics.DrawLine(linePen, origin, target);
     351      }
     352    }
    366353    #endregion
    367 
    368354    #region save image
    369355    private void saveImageToolStripMenuItem_Click(object sender, EventArgs e) {
     
    380366      Image image = new Bitmap(Width, Height);
    381367      using (Graphics g = Graphics.FromImage(image)) {
    382         int height = this.Height / tree.Depth;
    383         DrawFunctionTree(tree, g, 0, 0, Width, height);
     368        DrawFunctionTree(g, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
    384369      }
    385370      image.Save(filename);
     
    391376        using (Metafile file = new Metafile(filename, g.GetHdc())) {
    392377          using (Graphics emfFile = Graphics.FromImage(file)) {
    393             int height = this.Height / tree.Depth;
    394             DrawFunctionTree(tree, emfFile, 0, 0, Width, height);
     378            DrawFunctionTree(emfFile, preferredNodeWidth, preferredNodeHeight, minHorizontalDistance, minVerticalDistance);
    395379          }
    396380        }
     
    399383    }
    400384    #endregion
     385    #region export pgf/tikz
     386    private void exportLatexToolStripMenuItem_Click(object sender, EventArgs e) {
     387      var t = Tree;
     388      if (t == null) return;
     389      using (var dialog = new SaveFileDialog { Filter = "Tex (*.tex)|*.tex" }) {
     390        if (dialog.ShowDialog() != DialogResult.OK) return;
     391        string filename = dialog.FileName.ToLower();
     392        var formatter = new SymbolicExpressionTreeLatexFormatter();
     393        File.WriteAllText(filename, formatter.Format(t));
     394      }
     395    }
     396    #endregion
     397
     398    private void reingoldTilfordToolStripMenuItem_Click(object sender, EventArgs e) {
     399      minHorizontalDistance = 30;
     400      minVerticalDistance = 30;
     401      layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>(n => n.Subtrees) {
     402        NodeWidth = preferredNodeWidth,
     403        NodeHeight = preferredNodeHeight,
     404        HorizontalSpacing = minHorizontalDistance,
     405        VerticalSpacing = minVerticalDistance
     406      };
     407      reingoldTilfordToolStripMenuItem.Checked = true;
     408      boxesToolStripMenuItem.Checked = false;
     409      Repaint();
     410    }
     411
     412    private void boxesToolStripMenuItem_Click(object sender, EventArgs e) {
     413      minHorizontalDistance = 5;
     414      minVerticalDistance = 5;
     415      layoutEngine = new BoxesLayoutEngine<ISymbolicExpressionTreeNode>(n => n.Subtrees, n => n.GetLength(), n => n.GetDepth()) {
     416        NodeWidth = preferredNodeWidth,
     417        NodeHeight = preferredNodeHeight,
     418        HorizontalSpacing = minHorizontalDistance,
     419        VerticalSpacing = minVerticalDistance
     420      };
     421      reingoldTilfordToolStripMenuItem.Checked = false;
     422      boxesToolStripMenuItem.Checked = true;
     423      Repaint();
     424    }
    401425  }
    402426}
Note: See TracChangeset for help on using the changeset viewer.