Changeset 10496 for trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs
- Timestamp:
- 02/21/14 12:52:09 (11 years ago)
- Location:
- trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views
- Property svn:mergeinfo changed
/branches/HeuristicLab.ReingoldTilfordTreeLayout/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views (added) merged: 9970,9993,10434,10471,10487
- Property svn:mergeinfo changed
-
trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionTreeChart.cs
r9587 r10496 24 24 using System.Drawing; 25 25 using System.Drawing.Imaging; 26 using System.IO; 27 using System.Linq; 26 28 using System.Windows.Forms; 29 using Point = System.Drawing.Point; 27 30 28 31 namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views { 29 32 public partial class SymbolicExpressionTreeChart : UserControl { 30 33 private Image image; 31 private StringFormat stringFormat;34 private readonly StringFormat stringFormat; 32 35 private Dictionary<ISymbolicExpressionTreeNode, VisualSymbolicExpressionTreeNode> visualTreeNodes; 33 36 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 34 45 35 46 public SymbolicExpressionTreeChart() { … … 40 51 this.lineColor = Color.Black; 41 52 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(); 43 56 } 44 57 45 58 public SymbolicExpressionTreeChart(ISymbolicExpressionTree tree) 46 59 : this() { 60 layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>(); 61 layoutAdapter = new SymbolicExpressionTreeLayoutAdapter(); 47 62 this.Tree = tree; 48 63 } 49 64 65 #region Public properties 50 66 private int spacing; 51 67 public int Spacing { … … 106 122 set { suspendRepaint = value; } 107 123 } 124 #endregion 108 125 109 126 protected override void OnPaint(PaintEventArgs e) { … … 157 174 graphics.Clear(backgroundColor); 158 175 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); 161 177 } 162 178 } … … 250 266 251 267 #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)) { 322 329 linePen.DashStyle = visualLine.DashStyle; 323 graphics.DrawLine(linePen, connectionPoint, new Point(visualTreeNode.X + visualTreeNode.Width / 2, visualTreeNode.Y));330 graphics.DrawLine(linePen, origin, target); 324 331 } 325 }326 327 //calculate areas for the subtrees according to their tree size and call drawFunctionTree328 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 332 } 335 333 } … … 365 363 } 366 364 #endregion 367 368 365 #region save image 369 366 private void saveImageToolStripMenuItem_Click(object sender, EventArgs e) { … … 380 377 Image image = new Bitmap(Width, Height); 381 378 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); 384 380 } 385 381 image.Save(filename); … … 391 387 using (Metafile file = new Metafile(filename, g.GetHdc())) { 392 388 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); 395 390 } 396 391 } 397 392 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)); 398 403 } 399 404 }
Note: See TracChangeset
for help on using the changeset viewer.