#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);
}
}
}