#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.Drawing.Drawing2D; using System.Drawing.Text; using System.Linq; using HeuristicLab.Common; using HeuristicLab.Visualization; using Rectangle = HeuristicLab.Visualization.Rectangle; namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views { /// /// The SymbolicExpressionTreeTile will display either a full tree or just a subtree (case in which the SymbolicExpressionTreeProperty remains unset). /// By setting the Root property, the user can use the SymbolicExpressionTreeTile to layout and display arbitrary subtrees. /// public class SymbolicExpressionTreeTile : Group { public int PreferredNodeWidth { get; set; } public int PreferredNodeHeight { get; set; } private const int labelHeight = 30; public ReingoldTilfordLayoutEngine LayoutEngine { get; set; } private readonly Dictionary primitivesToNodes; private readonly Dictionary nodesToPrimitives; private string label; public string Label { get { return label; } set { label = value; } } private Size size; public Size Size { private set { size = value; } get { return size; } } private Point position; public Point Position { get { return position; } set { var oldpos = position; position = value; int ox = position.X - oldpos.X; int oy = position.Y - oldpos.Y; // translate all primitives to the new position foreach (var p in Primitives) { var rpb = p as RectangularPrimitiveBase; if (rpb != null) { var lowerLeft = new Point((int)Math.Floor(rpb.LowerLeft.X) + ox, (int)Math.Floor(rpb.LowerLeft.Y + oy)); var upperRight = new PointD(lowerLeft.X + rpb.Size.Width, lowerLeft.Y + rpb.Size.Height); rpb.SetPosition(lowerLeft, upperRight); } var line = p as LinearPrimitiveBase; if (line != null) { var start = new Point((int)Math.Floor(line.Start.X) + ox, (int)Math.Floor(line.Start.Y) + oy); var end = new Point(start.X + (int)line.Size.Width, start.Y + (int)line.Size.Height); line.SetPosition(start, end); } } } } public IPrimitive GetPrimitive(ISymbolicExpressionTreeNode node) { IPrimitive primitive; nodesToPrimitives.TryGetValue(node, out primitive); return primitive; } public ISymbolicExpressionTreeNode GetExpressionTreeNode(IPrimitive primitive) { ISymbolicExpressionTreeNode node; primitivesToNodes.TryGetValue(primitive, out node); return node; } private ISymbolicExpressionTree symbolicExpressionTree; public ISymbolicExpressionTree SymbolicExpressionTree { get { return symbolicExpressionTree; } set { symbolicExpressionTree = value; Root = symbolicExpressionTree.Root; } } new private void Clear() { Group.Clear(); primitivesToNodes.Clear(); nodesToPrimitives.Clear(); } private ISymbolicExpressionTreeNode root; public ISymbolicExpressionTreeNode Root { get { return root; } set { root = value; GeneratePrimitives(PreferredNodeWidth, PreferredNodeHeight); } } public SymbolicExpressionTreeTile(IChart chart) : base(chart) { primitivesToNodes = new Dictionary(); nodesToPrimitives = new Dictionary(); PreferredNodeWidth = 80; PreferredNodeHeight = 40; Group = new Group(chart); } public SymbolicExpressionTreeTile(IChart chart, ISymbolicExpressionTree tree) : this(chart) { SymbolicExpressionTree = tree; } private void GeneratePrimitives(double preferredNodeWidth, double preferredNodeHeight) { Clear(); var actualRoot = Root; if (Root.Symbol is ProgramRootSymbol && Root.SubtreeCount == 1) { actualRoot = Root.GetSubtree(0); } LayoutEngine.NodeWidth = PreferredNodeWidth; LayoutEngine.NodeHeight = PreferredNodeHeight; var visualNodes = LayoutEngine.CalculateLayout(actualRoot).ToList(); var font = new Font(FontFamily.GenericSansSerif, 10, GraphicsUnit.Pixel); var visualNodeMap = visualNodes.ToDictionary(x => x.Content, x => x); foreach (var visualNode in visualNodes) { var lowerLeft = new PointD(visualNode.X, visualNode.Y); var upperRight = new PointD(visualNode.X + preferredNodeWidth, visualNode.Y + preferredNodeHeight); var node = visualNode.Content; RectangularPrimitiveBase rectangularPrimitive; var shortenedLabel = ShortenLabel(node); if (node.SubtreeCount == 0) { rectangularPrimitive = new Rectangle(Chart, lowerLeft, upperRight) { Font = font, Text = shortenedLabel }; } else { rectangularPrimitive = new Ellipse(Chart, lowerLeft, upperRight) { Font = font, Text = shortenedLabel }; } primitivesToNodes.Add(rectangularPrimitive, node); // to be able to retrieve nodes via primitives nodesToPrimitives.Add(node, rectangularPrimitive); this.Add(rectangularPrimitive); if (rectangularPrimitive.Size.Width.IsAlmost(0) || rectangularPrimitive.Size.Height.IsAlmost(0)) { throw new Exception("Primitive size cannot be zero."); } } foreach (var node in visualNodes.Where(n => n.Content.SubtreeCount > 0)) { var parent = node.Content; foreach (var child in parent.Subtrees.Select(x => visualNodeMap[x])) { var start = new PointD(node.X + preferredNodeWidth / 2, node.Y + preferredNodeHeight); var end = new PointD(child.X + preferredNodeWidth / 2, child.Y); var line = new Line(this.Chart, start, end); this.Add(line); } } int xmin = 0, ymin = 0, xmax = 0, ymax = 0; foreach (var rpb in Primitives.OfType()) { if (xmin > rpb.LowerLeft.X) xmin = (int)Math.Floor(rpb.LowerLeft.X); if (xmax < rpb.UpperRight.X) xmax = (int)Math.Ceiling(rpb.UpperRight.X); if (ymin > rpb.LowerLeft.Y) ymin = (int)Math.Floor(rpb.LowerLeft.Y); if (ymax < rpb.UpperRight.Y) ymax = (int)Math.Ceiling(rpb.UpperRight.Y); } int width = xmax - xmin; int height = ymax - ymin; // draw a primitive to display the label var labelRect = new Rectangle(this.Chart, new PointD(0, height), new PointD(width, height + labelHeight)) { Pen = new Pen(Color.Black), Brush = new SolidBrush(Color.Transparent), Text = Label, Font = new Font(FontFamily.GenericSansSerif, 12) }; this.Add(labelRect); // draw a rectangle to mark the countour of this tile Size = new Size(width, height + labelHeight); var rectangle = new Rectangle(this.Chart, new PointD(0, 0), new PointD(Size.Width, Size.Height)) { Pen = new Pen(Color.Gray), Brush = new SolidBrush(Color.Transparent) }; this.Add(rectangle); // flip primitives vertically foreach (var primitive in Primitives) { var rpb = primitive as RectangularPrimitiveBase; if (rpb != null) { rpb.SetPosition(rpb.LowerLeft.X, Size.Height - rpb.UpperRight.Y, rpb.UpperRight.X, Size.Height - rpb.LowerLeft.Y); } else { var line = primitive as LinearPrimitiveBase; if (line != null) { line.SetPosition(line.Start.X, Size.Height - line.Start.Y, line.End.X, Size.Height - line.End.Y); } } } } public override void Draw(Graphics graphics) { graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.TextRenderingHint = TextRenderingHint.AntiAlias; base.Draw(graphics); } private string ShortenLabel(ISymbolicExpressionTreeNode node) { var term = node as SymbolicExpressionTreeTerminalNode; if (term != null) { var parts = term.ToString().Split(' '); // split by space if (parts.Length == 1) { return parts[0].Substring(0, 5); } else { return parts[0].Substring(0, 5) + parts[1]; } } return node.Symbol.ToString().Substring(0, 3); } } }