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 } }