#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 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.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using HeuristicLab.Common;
using HeuristicLab.Core.Networks;
using HeuristicLab.Visualization;
namespace HeuristicLab.Networks.Views.NetworkVisualization {
[Primitive(typeof(IPort), true)]
public class PortRectangle : FixedSizeRectangle, INetworkItemPrimitive {
private const int size = 10;
protected readonly INodeVisualProperties nodeVisualProperties;
protected IPortVisualProperties VisualProperties {
get { return (IPortVisualProperties)networkItem.VisualProperties; }
set {
if (networkItem.VisualProperties == value) return;
networkItem.VisualProperties = value;
}
}
protected bool IgnoreVisualPropertiesChanges { get; set; }
public override Size Size {
get {
return new Size((int)Math.Round(size * Chart.WorldToPixelRatio.Width),
(int)Math.Round(size * Chart.WorldToPixelRatio.Height));
}
}
protected IPort networkItem;
public IPort NetworkItem {
get { return networkItem; }
set {
if (networkItem == value) return;
if (networkItem != null) DeregisterNetworkItemEvents();
networkItem = value;
if (networkItem != null) RegisterNetworkItemEvents();
LoadVisualProperties();
OnNetworkItemChanged();
}
}
public override Brush Brush {
get { return base.Brush; }
set {
if (base.Brush == value) return;
base.Brush = value;
SaveVisualProperties();
}
}
public override Pen Pen {
get { return base.Pen; }
set {
if (base.Pen == value) return;
base.Pen = value;
SaveVisualProperties();
}
}
public PortRectangle(IChart chart, IPort port, INode node)
: base(chart, PointD.Empty, Size.Empty) {
nodeVisualProperties = (INodeVisualProperties)node.VisualProperties;
nodeVisualProperties.Changed += NodeVisualProperties_Changed;
NetworkItem = port;
}
protected virtual void RegisterNetworkItemEvents() {
if (VisualProperties != null) {
VisualProperties.Changed += VisualProperties_Changed;
}
networkItem.NameChanged += NetworkItem_NameChanged;
var connectablePort = networkItem as IConnectablePort;
if (connectablePort != null) {
connectablePort.ConnectedPortChanged += Port_ConnectedPortChanged;
}
}
protected virtual void DeregisterNetworkItemEvents() {
if (VisualProperties != null) {
VisualProperties.Changed -= VisualProperties_Changed;
}
networkItem.NameChanged -= NetworkItem_NameChanged;
var connectablePort = networkItem as IConnectablePort;
if (connectablePort != null) {
connectablePort.ConnectedPortChanged -= Port_ConnectedPortChanged;
}
}
#region Overrides
public override void SetPosition(PointD point) {
base.SetPosition(point);
SaveVisualProperties();
}
public override void Draw(Graphics graphics) {
base.Draw(graphics);
var p = Chart.TransformWorldToPixel(Point);
if (networkItem != null && VisualProperties != null) {
using (var font = new Font(FontFamily.GenericSansSerif, (float)Math.Round(8.25 * Chart.WorldToPixelRatio.Width))) {
var textSize = graphics.MeasureString(networkItem.Name, font);
var vp = VisualProperties;
switch (vp.Orientation) {
case Orientation.North: p = new Point((int)(p.X - textSize.Width / 2), (int)(p.Y - Size.Height - textSize.Height)); break;
case Orientation.East: p = new Point(p.X + Size.Width, (int)(p.Y - textSize.Height / 2)); break;
case Orientation.South: p = new Point((int)(p.X - textSize.Width / 2), p.Y + Size.Height); break;
case Orientation.West: p = new Point((int)(p.X - Size.Width - textSize.Width), (int)(p.Y - textSize.Height / 2)); break;
}
graphics.DrawString(networkItem.Name, font, Brushes.Black, p);
}
}
}
public override void PostDraw(Graphics graphics) {
base.PostDraw(graphics);
if (Selected) {
var p = Chart.TransformWorldToPixel(Point);
using (var pen = new Pen(Color.LightGray, 3) { DashStyle = DashStyle.Dash })
graphics.DrawRectangle(pen, p.X - Size.Width / 2, p.Y - Size.Height / 2, Size.Width, Size.Height);
}
}
#endregion
#region Events
public event EventHandler NetworkItemChanged;
protected virtual void OnNetworkItemChanged() {
var handler = NetworkItemChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
#endregion
#region Event Handlers
private void NodeVisualProperties_Changed(object sender, EventArgs e) {
if (IgnoreVisualPropertiesChanges) return;
LoadVisualProperties();
}
private void VisualProperties_Changed(object sender, EventArgs e) {
if (IgnoreVisualPropertiesChanges) return;
LoadVisualProperties();
}
private void NetworkItem_NameChanged(object sender, EventArgs e) {
OnRedrawRequired();
}
private void Port_ConnectedPortChanged(object sender, EventArgs e) {
var connectionLines = Chart.Group.GetAllPrimitives(Point).OfType();
var outgoingLines = connectionLines.Where(x => x.StartPortRectangle == this);
foreach (var l in outgoingLines) Chart.Group.Remove(l);
var connectablePort = (IConnectablePort)networkItem;
var connectedPort = connectablePort.ConnectedPort as IConnectablePort;
if (connectedPort == null) return;
var nodeRectangles = Chart.Group.Primitives.OfType();
PortRectangle endPortRectangle = null;
foreach (var nodeRectangle in nodeRectangles) {
endPortRectangle = (PortRectangle)nodeRectangle.GetPortPrimitive(connectedPort);
if (endPortRectangle != null) break;
}
if (endPortRectangle == null) return;
var connectionShape = new ConnectionLine(Chart, this, endPortRectangle);
Chart.Group.Add(connectionShape);
}
#endregion
#region Helpers
private void LoadVisualProperties() {
if (VisualProperties == null)
VisualProperties = new PortVisualProperties();
var vp = VisualProperties;
IgnoreVisualPropertiesChanges = true;
try {
var p = PointD.Empty;
double nodeWidth = nodeVisualProperties.UpperRight.X - nodeVisualProperties.LowerLeft.X;
double nodeHeight = nodeVisualProperties.UpperRight.Y - nodeVisualProperties.LowerLeft.Y;
switch (vp.Orientation) {
case Orientation.North:
p.X = nodeVisualProperties.LowerLeft.X + vp.Offset * nodeWidth;
p.Y = nodeVisualProperties.UpperRight.Y;
break;
case Orientation.East:
p.X = nodeVisualProperties.UpperRight.X;
p.Y = nodeVisualProperties.UpperRight.Y - vp.Offset * nodeHeight;
break;
case Orientation.South:
p.X = nodeVisualProperties.UpperRight.X - vp.Offset * nodeWidth;
p.Y = nodeVisualProperties.LowerLeft.Y;
break;
case Orientation.West:
p.X = nodeVisualProperties.LowerLeft.X;
p.Y = nodeVisualProperties.LowerLeft.Y + vp.Offset * nodeHeight;
break;
}
SetPosition(p);
Brush = new SolidBrush(vp.BrushColor);
Pen = new Pen(vp.PenColor);
} finally { IgnoreVisualPropertiesChanges = false; }
}
private void SaveVisualProperties() {
if (networkItem == null || IgnoreVisualPropertiesChanges) return;
var vp = VisualProperties;
IgnoreVisualPropertiesChanges = true;
try {
var lowerLeft = nodeVisualProperties.LowerLeft;
var upperRight = nodeVisualProperties.UpperRight;
if (lowerLeft.X <= Point.X && Point.X <= upperRight.X) {
// check N/S
if (Point.Y.IsAlmost(upperRight.Y)) {
vp.Orientation = Orientation.North;
vp.Offset = (Point.X - lowerLeft.X) / (upperRight.X - lowerLeft.X);
} else if (Point.Y.IsAlmost(lowerLeft.Y)) {
vp.Orientation = Orientation.South;
vp.Offset = (upperRight.X - Point.X) / (upperRight.X - lowerLeft.X);
}
}
if (lowerLeft.Y <= Point.Y && Point.Y <= upperRight.Y) {
// check E/W
if (Point.X.IsAlmost(upperRight.X)) {
vp.Orientation = Orientation.East;
vp.Offset = (upperRight.Y - Point.Y) / (upperRight.Y - lowerLeft.Y);
} else if (Point.X.IsAlmost(lowerLeft.X)) {
vp.Orientation = Orientation.West;
vp.Offset = (Point.Y - lowerLeft.Y) / (upperRight.Y - lowerLeft.Y);
}
}
vp.BrushColor = ((SolidBrush)Brush).Color;
vp.PenColor = Pen.Color;
} finally { IgnoreVisualPropertiesChanges = false; }
}
#endregion
}
}