#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 = 60; private readonly SolidBrush defaultBrush; private readonly Pen defaultPen; private readonly Font defaultFont; 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() { base.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); defaultBrush = new SolidBrush(Color.Transparent); defaultPen = new Pen(Color.DimGray); defaultFont = new Font(FontFamily.GenericSansSerif, 12, GraphicsUnit.Pixel); } 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).GetSubtree(0); } LayoutEngine.NodeWidth = PreferredNodeWidth; LayoutEngine.NodeHeight = PreferredNodeHeight; var visualNodes = LayoutEngine.CalculateLayout(actualRoot).ToList(); 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 = defaultFont, Text = shortenedLabel, Brush = defaultBrush, Pen = defaultPen, MaximumFontSize = 12f }; } else { rectangularPrimitive = new Ellipse(Chart, lowerLeft, upperRight) { // Font = defaultFont, Text = shortenedLabel, Brush = defaultBrush, Pen = defaultPen, MaximumFontSize = 12f }; } 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) { Pen = defaultPen }; 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 = defaultPen, Brush = defaultBrush, // Text = Label, // Font = defaultFont }; 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 = defaultPen, Brush = defaultBrush }; this.Add(rectangle); // flip primitives vertically foreach (var primitive in Primitives) { var rpb = primitive as RectangularPrimitiveBase; if (rpb != null) { var lowerLeft = new PointD(rpb.LowerLeft.X, Size.Height - rpb.UpperRight.Y); var upperRight = new PointD(rpb.UpperRight.X, Size.Height - rpb.LowerLeft.Y); rpb.SetPosition(lowerLeft, upperRight); } else { var line = primitive as LinearPrimitiveBase; if (line != null) { var lowerLeft = new PointD(line.Start.X, Size.Height - line.Start.Y); var upperRight = new PointD(line.End.X, Size.Height - line.End.Y); line.SetPosition(lowerLeft, upperRight); } } } } 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 return parts.Length == 1 ? string.Format("{0:0.000}", double.Parse(parts[0])) : string.Format("{0:0.000} {1}", double.Parse(parts[0]), parts[1]); } return node.Symbol.ToString().Substring(0, 3); } } }