using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Microsoft.Msagl.Core.DataStructures; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Layout; using Microsoft.Msagl.DebugHelpers; namespace Microsoft.Msagl.Miscellaneous.LayoutEditing { /// /// pushes the nodes it got bumped to: pushes horizontally or vertically /// public class BumperPusher { readonly double separation; readonly Set fixedNodes = new Set(); readonly RTree rtree; Node[] pushingNodes; /// /// /// /// nodes that are being pushed /// /// public BumperPusher(IEnumerable pushedNodes, double separation, Node[] pushingNodes) { this.separation = separation; rtree = new RTree(RectangleNode.CreateRectangleNodeOnEnumeration( pushedNodes.Select(n => new RectangleNode(n, GetPaddedBoxOfNode(n))))); //LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(rtree.GetAllLeaves().Select(n=>new DebugCurve(n.BoundaryCurve))); this.pushingNodes = pushingNodes; } internal IEnumerable FixedNodes { get { return fixedNodes; } } Rectangle GetPaddedBoxOfNode(Node n) { #if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369 there are no structs in js var ret = n.BoundaryCurve.BoundingBox.Clone(); #else var ret = n.BoundaryCurve.BoundingBox; #endif ret.Pad(separation/2); return ret; } /// /// /// /// public IList PushNodes() { fixedNodes.Clear(); fixedNodes.InsertRange(pushingNodes); var q = new Queue(pushingNodes); var ret = new List(); while (q.Count > 0) { var n = q.Dequeue(); foreach (var node in PushByNodeAndReportPushedAsFixed(n)) { q.Enqueue(node); fixedNodes.Insert(node); ret.Add(node); } } return ret; } IEnumerable PushByNodeAndReportPushedAsFixed(Node pushingNode) { var ret = new List(); var pushingNodeBox = GetPaddedBoxOfNode(pushingNode); foreach (var rectNode in rtree.GetAllLeavesIntersectingRectangle(pushingNodeBox)) { if (fixedNodes.Contains(rectNode.UserData)) continue; if (PushNodeAndUpdateRTree(pushingNode, rectNode)) ret.Add(rectNode.UserData); } return ret; } bool PushNodeAndUpdateRTree(Node pushingNode, RectangleNode pushed) { var del = pushed.UserData.Center - pushingNode.Center; var w = pushingNode.Width / 2 + pushed.UserData.Width / 2; var h = pushingNode.Height / 2 + pushed.UserData.Height / 2; var absDelXBetweenCenters = Math.Abs(del.X); var absDelYBetweenCenters = Math.Abs(del.Y); var xSep = absDelXBetweenCenters - w; var ySep = absDelYBetweenCenters - h; if (xSep >= separation || ySep >= separation) return false; if (absDelXBetweenCenters >= absDelYBetweenCenters) { double d = del.X > 0 ? separation - xSep : xSep - separation; PushByX(d, pushed); } else { double d = del.Y > 0 ? separation - ySep : ySep - separation; PushByY(d, pushed); } UpdateBoundingBoxesOfPushedAndUpParents(pushed); return true; } void PushByX(double del, RectangleNode pushed) { var delPoint = new Point(del, 0); PushByPoint(pushed, delPoint); } static void PushByPoint(RectangleNode pushed, Point delPoint) { pushed.UserData.Center += delPoint; var cluster = pushed.UserData as Cluster; if (cluster != null) cluster.DeepContentsTranslation(delPoint, true); } void PushByY(double del, RectangleNode pushed) { var delPoint = new Point(0, del); PushByPoint(pushed, delPoint); } void UpdateBoundingBoxesOfPushedAndUpParents(RectangleNode pushed) { pushed.Rectangle = GetPaddedBoxOfNode(pushed.UserData); var parent = pushed.Parent; while (parent != null) { parent.Rectangle = new Rectangle(parent.Left.Rectangle, parent.Right.Rectangle); parent = parent.Parent; } } /// /// /// /// /// public void UpdateRTreeByChangedNodeBox(Node cluster, Rectangle previousBox) { RectangleNode rectNode = FindClusterNode(cluster, previousBox); UpdateBoundingBoxesOfPushedAndUpParents(rectNode); } RectangleNode FindClusterNode(Node cluster, Rectangle previousBox) { var node = rtree.RootNode; return FindClusterNodeRecurse(node, cluster, previousBox); } RectangleNode FindClusterNodeRecurse(RectangleNode node, Node cluster, Rectangle previousBox) { if (node.UserData != null) return node.UserData == cluster ? node : null; RectangleNode n0=null; if (previousBox.Intersects(node.Left.Rectangle)) n0 = FindClusterNodeRecurse(node.Left, cluster, previousBox); if (n0 != null) return n0; if (previousBox.Intersects(node.Right.Rectangle)) return FindClusterNodeRecurse(node.Right, cluster, previousBox); return null; } /// /// /// /// public Node FirstPushingNode() { return pushingNodes[0]; } } }