using System;
using System.Collections.Generic;
using Microsoft.Msagl.Core.DataStructures;
using System.Diagnostics;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Layout.Incremental;
namespace Microsoft.Msagl.Prototype.NonOverlappingBoundaries {
///
/// A CvxHull is Convex hull
///
public abstract class CvxHull : IHull {
///
/// We have a hierarchy of membership
///
public ClusterConvexHull Parent { get; protected set; }
///
/// see IHull
///
public abstract Point Center { get; }
///
/// move the center by delta
///
///
public abstract void MoveCenter(Point delta);
///
/// see IHull
///
public abstract RectangleNode RectangleNode { get; }
///
///
///
public abstract double Weight { get; }
///
/// Gets the boundary translated to the current Center
///
///
public abstract Polyline TranslatedBoundary();
///
/// Resolves overlap between this and another CHull by moving on the minimum penetration depth vector
///
///
///
public double Project(IHull other) {
var v = other as CvxHull;
var vc = v as ClusterConvexHull;
var c = this as ClusterConvexHull;
if (c!=null && c.Contains(v)) {
return 0;
}
if (vc != null && vc.Contains(this)) {
return 0;
}
Debug.Assert(v != null);
Point pd = PenetrationDepth.PenetrationDepthForPolylines(TranslatedBoundary(), v.TranslatedBoundary());
if (pd.Length > 0) {
Point wpd = pd / (Weight + v.Weight);
MoveCenter(v.Weight * wpd);
v.MoveCenter(-Weight * wpd);
}
return pd.Length;
}
}
///
/// A CvxHull for rectangles
///
public class RCHull : CvxHull {
internal Node mNode;
private Polyline boundary;
private Polyline translatedBoundary;
double w2, h2;
///
/// Center of the node
///
public override Point Center {
get { return mNode.Center; }
}
///
/// Move by delta
///
///
public override void MoveCenter(Point delta) {
mNode.Center += delta;
}
///
/// RectangleNode is used in region queries
///
public override RectangleNode RectangleNode {
get {
var r = new Rectangle(Center.X - w2, Center.Y - h2, Center.X + w2, Center.Y + h2);
return new RectangleNode(this, r);
}
}
///
///
///
public override double Weight {
get { return ((FiNode)mNode.AlgorithmData).stayWeight; }
}
///
///
///
///
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m")]
public RCHull(ClusterConvexHull parent, Node mNode, double padding) {
//var node = (FastIncrementalLayout.Node)mNode.AlgorithmData;
//this.w2 = node.width / 2.0 + padding;
//this.h2 = node.height / 2.0 + padding;
this.w2 = mNode.Width / 2.0 + padding;
this.h2 = mNode.Height / 2.0 + padding;
this.Parent = parent;
boundary = new Polyline(new Point[]{
new Point(-w2, -h2),
new Point(-w2, h2),
new Point(w2, h2),
new Point(w2, -h2)
});
boundary.Closed = true;
translatedBoundary = new Polyline(boundary);
translatedBoundary.Closed = true;
this.mNode = mNode;
}
///
/// Gets the boundary translated to the current Center
///
///
public override Polyline TranslatedBoundary() {
PolylinePoint qq = translatedBoundary.StartPoint;
for (PolylinePoint pp = boundary.StartPoint; pp != null; pp = pp.Next) {
qq.Point = pp.Point + Center;
qq = qq.Next;
}
return translatedBoundary;
}
}
///
/// The convex hull of the constituents of a Cluster
///
public class ClusterConvexHull : CvxHull {
internal Cluster cluster;
///
/// The Barycenter of the cluster
///
public override Point Center {
get { return cluster.SetBarycenter(); }
}
///
/// Move contents by delta
///
///
public override void MoveCenter(Point delta)
{
cluster.ForEachNode(v => v.Center += delta);
}
///
///
///
public override double Weight {
get { return cluster.Weight; }
}
///
/// Bounding box used in region queries
///
public override RectangleNode RectangleNode {
get {
var r = new Rectangle(Center);
foreach (var node in cluster.Nodes)
r.Add(node.BoundingBox);
return new RectangleNode(this, r);
}
}
///
/// The convex hull of the constituents of a Cluster
///
///
///
public ClusterConvexHull(Cluster cluster, ClusterConvexHull parent) {
this.cluster = cluster;
this.Parent = parent;
}
///
/// Gets the boundary translated to the current Center
///
///
public override Polyline TranslatedBoundary() {
return ComputeConvexHull();
}
///
/// The convex hull of all the points of all the nodes in the cluster
///
private Polyline ComputeConvexHull()
{
var points = new List();
foreach (Node v in cluster.Nodes)
{
CvxHull r = new RCHull(null, v, 0);
foreach (PolylinePoint p in r.TranslatedBoundary().PolylinePoints)
points.Add(p.Point);
}
foreach (Cluster c in cluster.Clusters)
{
points.AddRange(new ClusterConvexHull(c, this).TranslatedBoundary());
}
return new Polyline(ConvexHull.CalculateConvexHull(points)) {Closed = true};
}
///
/// Search hierarchy to check if child is a descendent of this.
///
///
/// true if child is a descendent of this cluster
public bool Contains(CvxHull child) {
if (child.Parent == null) {
return false;
}
if (child.Parent == this) {
return true;
}
return Contains(child.Parent);
}
}
///
/// Prevents the boundaries of nodes and clusters from overlapping
///
public class AllPairsNonOverlappingBoundaries : IConstraint {
List hulls = new List();
private void traverseClusters(ClusterConvexHull parent, Cluster cluster, double padding) {
ClusterConvexHull hull = new ClusterConvexHull(cluster, parent);
hulls.Add(hull);
foreach (var v in cluster.nodes) {
hulls.Add(new RCHull(hull, v, padding));
}
foreach (var c in cluster.clusters) {
traverseClusters(hull, c, padding);
}
}
///
///
/// Non-overlap between nodes in the same cluster (or the root cluster), between the convex hulls
/// of clusters and nodes that do belong to those clusters and between clusters and clusters.
///
///
/// for padding extra space around nodes
public AllPairsNonOverlappingBoundaries(Cluster cluster, FastIncrementalLayoutSettings settings) {
foreach (var v in cluster.nodes) {
hulls.Add(new RCHull(null,v, settings.NodeSeparation));
}
foreach (var c in cluster.clusters) {
traverseClusters(null, c, settings.NodeSeparation);
}
}
#region IConstraint Members
private static int AllPairsComputationLimit = 20;
///
/// Uses Lev's fast proximity query to find pairs of nodes/clusters with overlapping bounding boxes.
/// When such are found, they are projected apart.
///
public double Project() {
double displacement = 0;
if (hulls.Count < AllPairsComputationLimit) {
// if there are only a few nodes then do it the most straightforward n^2 way
for (int i = 0; i < hulls.Count - 1; ++i) {
IHull u = hulls[i];
for (int j = i + 1; j < hulls.Count; ++j) {
displacement += u.Project(hulls[j]);
}
}
} else {
var pq = new ProximityQuery(hulls);
List> closePairs = pq.GetAllIntersections();
//shuffle(ref closePairs);
foreach (var k in closePairs) {
displacement += k.Item1.Project(k.Item2);
}
}
return displacement;
}
//void shuffle(ref List l) {
// List tmpList = new List();
// while (l.Count > 0) {
// int i = rand.Next(l.Count);
// tmpList.Add(l[i]);
// l.RemoveAt(i);
// }
// l = tmpList;
//}
///
/// NonOverlap constraints are a beautification thing, and therefore higher level than others
///
/// 2
public int Level { get { return 2; } }
#endregion
#region IConstraint Members
///
///
///
public IEnumerable Nodes {
get { return new List(); }
}
#endregion
}
}