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