public MstLineSweeper(List<Tuple<int, int, double, double, double>> proximityEdges, Size[] nodeSizes, Point[] nodePositions, bool forLayers) { _proximityEdges = proximityEdges; _nodeSizes = nodeSizes; _nodePositions = nodePositions; _forLayers = forLayers; Debug.Assert(nodePositions.Length==nodeSizes.Length); _q = new BinaryHeapPriorityQueue(nodeSizes.Length*2); }
OverlapRemoval(OverlapRemovalSettings settings, Node[] nodes, Size[] sizes) { _overlapForLayers = true; _settings = settings; _sizes = sizes; _nodes = nodes; }
/* /// <summary> /// Add additional proximity edges which where not found by the triangulation. /// </summary> /// <param name="proximityEdges">tuple representing an edge with more information: nodeId1, nodeId2,expandingFactor t, ideal distance, weight</param> /// <param name="nodeSizes"></param> /// <param name="nodePositions"></param> /// <returns></returns> int CreateProximityEdgesWithRTree(List<Tuple<int, int, double, double, double>> proximityEdges, Size[] nodeSizes, Point[] nodePositions) { HashSet<Tuple<int, int>> edgeSet = new HashSet<Tuple<int, int>>(); foreach (var proximityEdge in proximityEdges) { edgeSet.Add(Tuple.Create(proximityEdge.Item1, proximityEdge.Item2)); } RectangleNode<int> rootNode = RectangleNode<int>.CreateRectangleNodeOnEnumeration( nodeSizes.Select( (size, index) => new RectangleNode<int>(index, new Rectangle(size, nodePositions[index])))); int numCrossings = 0; RectangleNodeUtils.CrossRectangleNodes<int, int>(rootNode, rootNode, (a, b) => { if (a == b) return; var tuple = GetIdealEdgeLength ( a, b, nodePositions[a ], nodePositions[b ], nodeSizes); Tuple<int, int> setTuple; if (!(tuple.Item3 > 1) || edgeSet.Contains(setTuple = new Tuple<int, int>(tuple.Item1, tuple.Item2))) return; proximityEdges.Add(tuple); edgeSet.Add(setTuple); numCrossings++; }); return numCrossings; } */ /// <summary> /// Returns a tuple representing an edge with: nodeId1, nodeId2, t(overlapFactor), ideal distance, edge weight. /// </summary> /// <param name="nodeId1"></param> /// <param name="nodeId2"></param> /// <param name="point1"></param> /// <param name="point2"></param> /// <param name="nodeSizes"></param> /// <param name="forLayers"></param> /// <returns></returns> internal static Tuple<int, int, double, double, double> GetIdealEdgeLength(int nodeId1, int nodeId2, Point point1, Point point2, Size[] nodeSizes, bool forLayers) { double t; double idealDist = GetIdealEdgeLength(nodeId1, nodeId2, point1, point2, nodeSizes, out t); double length = (point1 - point2).Length; Rectangle box1, box2; if (forLayers) { int maxId = Math.Max(nodeId1, nodeId2); box1 = new Rectangle(nodeSizes[maxId], point1); box2 = new Rectangle(nodeSizes[maxId], point2); } else { box1 = new Rectangle(nodeSizes[nodeId1], point1); box2 = new Rectangle(nodeSizes[nodeId2], point2); } var distBox = GetDistanceRects(box1, box2); double weight; if (t > 1) //overlap weight = -(idealDist - length); else weight = distBox; int smallId = nodeId1; int bigId = nodeId2; if (nodeId1 > nodeId2) { smallId = nodeId2; bigId = nodeId1; } return Tuple.Create(smallId, bigId, t, idealDist, weight); }
int FindProximityEdgesWithSweepLine(List<Tuple<int, int, double, double, double>> proximityEdges, Size[] nodeSizes, Point[] nodePositions) { MstLineSweeper mstLineSweeper = new MstLineSweeper(proximityEdges, nodeSizes, nodePositions, _overlapForLayers); return mstLineSweeper.Run(); }
/// <summary> /// Does one iterations in which a miniminum spanning tree is /// determined on the delaunay triangulation and finally the tree is exanded to resolve the overlaps. /// </summary> /// <param name="nodePositions"></param> /// <param name="nodeSizes"></param> /// <param name="scanlinePhase"></param> /// <returns></returns> bool OneIteration(Point[] nodePositions, Size[] nodeSizes, bool scanlinePhase) { var cdt = new Cdt(nodePositions.Select((p, index) => Tuple.Create(p, (object) index))); cdt.Run(); var siteIndex = new Dictionary<CdtSite, int>(); for (int i = 0; i < nodePositions.Length; i++) siteIndex[cdt.PointsToSites[nodePositions[i]]] = i; int numCrossings = 0; List<Tuple<int, int, double, double, double>> proximityEdges = new List<Tuple<int, int, double, double, double>>(); foreach (var site in cdt.PointsToSites.Values) foreach (var edge in site.Edges) { Point point1 = edge.upperSite.Point; Point point2 = edge.lowerSite.Point; var nodeId1 = siteIndex[edge.upperSite]; var nodeId2 = siteIndex[edge.lowerSite]; Debug.Assert(ApproximateComparer.Close(point1, nodePositions[nodeId1])); Debug.Assert(ApproximateComparer.Close(point2, nodePositions[nodeId2])); var tuple = GetIdealEdgeLength(nodeId1, nodeId2, point1, point2, nodeSizes, _overlapForLayers); proximityEdges.Add(tuple); if (tuple.Item3 > 1) numCrossings++; } if (numCrossings == 0 || scanlinePhase) { int additionalCrossings = FindProximityEdgesWithSweepLine(proximityEdges, nodeSizes, nodePositions); if (numCrossings == 0 && additionalCrossings == 0) { // if(nodeSizes.Length>100) // ShowAndMoveBoxesRemoveLater(null, proximityEdges, nodeSizes, nodePositions, -1); return false; } if (numCrossings == 0 && !scanlinePhase) return false; } var treeEdges = MstOnDelaunayTriangulation.GetMstOnTuple(proximityEdges, nodePositions.Length); int rootId = treeEdges.First().Item1; // if (nodeSizes.Length > 100) // ShowAndMoveBoxesRemoveLater(treeEdges, proximityEdges, nodeSizes, nodePositions, rootId); MoveNodePositions(treeEdges, nodePositions, rootId); return true; }
/// <summary> /// adding a point with a Size /// </summary> /// <param name="size"></param> /// <param name="point"></param> public void Add(Size size, Point point) { var w = size.Width / 2; var h = size.Height / 2; Add(new Point(point.X - w, point.Y - h)); Add(new Point(point.X + w, point.Y - h)); Add(new Point(point.X - w, point.Y + h)); Add(new Point(point.X + w, point.Y + h)); }
/// <summary> /// Determines the edges of the triangulation together with their desired length (distance between nodes). /// </summary> /// <param name="originalGraph"></param> /// <param name="cdt"></param> /// <param name="targetSizes"></param> /// <param name="desiredEdgeDistances"></param> /// <returns></returns> public static int GetProximityEdgesWithDistance(Node[] originalGraph, Cdt cdt, Size[] targetSizes, out List<Tuple<int, int, double, double>> desiredEdgeDistances) { desiredEdgeDistances = new List<Tuple<int, int, double, double>>(); int numberOverlappingPairs = 0; var edgeSet = new HashSet<CdtEdge>(); //add edges foreach (CdtTriangle triangle in cdt.GetTriangles()) { foreach (CdtEdge triangleEdge in triangle.Edges) { CdtSite site1 = triangleEdge.upperSite; CdtSite site2 = triangleEdge.lowerSite; var nodeId1 = (int) site1.Owner; var nodeId2 = (int) site2.Owner; if (edgeSet.Contains(triangleEdge)) continue; //edge already included edgeSet.Add(triangleEdge); Point point1 = site1.Point; Point point2 = site2.Point; double t; double distance = GetIdealDistanceBetweenNodes(nodeId1, nodeId2, point1, point2, targetSizes, out t); if (t > 1) numberOverlappingPairs++; int nodeIdSmall = nodeId1; int nodeIdBig = nodeId2; if (nodeId1 > nodeId2) { nodeIdSmall = nodeId2; nodeIdBig = nodeId1; } var tuple = new Tuple<int, int, double, double>(nodeIdSmall, nodeIdBig, distance, t); desiredEdgeDistances.Add(tuple); } } return numberOverlappingPairs; }
/// <summary> /// Does the initial scaling of the layout, could also be avoided. /// </summary> /// <param name="nodes"></param> /// <param name="nodePositions"></param> /// <param name="nodeSizes"></param> /// <param name="scalingMethod"></param> static void DoInitialScaling(Node[] nodes, Point[] nodePositions, Size[] nodeSizes, InitialScaling scalingMethod) { var avgEdgeLength = AvgEdgeLength(nodes); double goalLength; if (scalingMethod == InitialScaling.Inch72Pixel) goalLength = 72; else if (scalingMethod == InitialScaling.AvgNodeSize) goalLength = nodeSizes.Average(box => (box.Width + box.Height)/2); else return; double scaling = goalLength/avgEdgeLength; #if DEBUG Console.WriteLine("AvgEdgeLength Scaling Method: {0}, ScaleFactor={1:F2}", scalingMethod, scaling); #endif for (int j = 0; j < nodePositions.Length; j++) { nodePositions[j] *= scaling; } }
int CountCrossingsWithRTree(Size[] nodeSizes) { RectangleNode<int> rootNode = RectangleNode<int>.CreateRectangleNodeOnEnumeration( nodeSizes.Select((r, index) => new RectangleNode<int>(index, new Rectangle(r,nodePositions[index])))); int numCrossings = 0; RectangleNodeUtils.CrossRectangleNodes<int, int>(rootNode, rootNode, (a, b) => { if (a == b) return; numCrossings++; }); return numCrossings; }
internal static Point[] InitNodePositionsAndBoxes(OverlapRemovalSettings overlapRemovalSettings, Node[] nodes, out Point[] nodePositions, out Size[] nodeSizes) { nodePositions = nodes.Select(v => v.Center).ToArray(); //make sure no two points are the same RandomizePoints(nodePositions, new Random(overlapRemovalSettings.RandomizationSeed), overlapRemovalSettings.Epsilon, overlapRemovalSettings.RandomizeAllPointsOnStart); nodeSizes = GetNodeSizesByPaddingWithHalfSeparation(nodes, overlapRemovalSettings.NodeSeparation); return nodePositions; }
// double GetAverageOverlap(List<Tuple<int, int, double, double>> proximityEdgesWithDistance, // Point[] positions, Rectangle[] rectangles) { // double overlap = 0; // int counter = 0; // foreach (Tuple<int, int, double, double> tuple in proximityEdgesWithDistance) { // int nodeId1 = tuple.Item1; // int nodeId2 = tuple.Item2; // Point point1 = positions[nodeId1]; // Point point2 = positions[nodeId2]; // // if (nodeBoxes == null) throw new ArgumentNullException("nodeBoxes"); // if (nodeBoxes.Length <= nodeId1) return 0; // if (nodeBoxes.Length <= nodeId2) return 0; // double box1Width = nodeBoxes[nodeId1].Width; // double box1Height = nodeBoxes[nodeId1].Height; // double box2Width = nodeBoxes[nodeId2].Width; // double box2Height = nodeBoxes[nodeId2].Height; // // //Gansner et. al Scaling factor of distance // double tw = (box1Width/2 + box2Width/2)/Math.Abs(point1.X - point2.X); // double th = (box1Height/2 + box2Height/2)/Math.Abs(point1.Y - point2.Y); // double t = Math.Max(Math.Min(tw, th), 1); // // if (t == 1) continue; // no overlap between the bounding boxes // // double distance = (t - 1)*(point1 - point2).Length; // overlap += distance; // counter++; // } // // overlap /= counter; // return overlap; // } /// <summary> /// For debugging only /// </summary> /// <param name="currentIteration"></param> /// <param name="nodeSizes"></param> /// <param name="nodePositions"></param> /// <param name="newPositions"></param> /// <param name="proximityEdgesWithDistance"></param> /// <param name="finalGridVectors"></param> static void ShowCurrentMovementVectors(int currentIteration, Size[] nodeSizes, Point[] nodePositions, List<Point> newPositions, List<Tuple<int, int, double, double>> proximityEdgesWithDistance, Point[] finalGridVectors) { #if DEBUG && ! SILVERLIGHT && !SHARPKIT if (DebugMode && currentIteration%1 == 0) { List<DebugCurve> curveList = new List<DebugCurve>(); var nodeBoxes = new Rectangle[nodeSizes.Length]; for(int i=0;i<nodeBoxes.Length;i++) nodeBoxes[i]=new Rectangle(nodeSizes[i], nodePositions[i]); var nodeCurves = nodeBoxes.Select( v => new DebugCurve(220, 1, "black", Curve.PolylineAroundClosedCurve(CurveFactory.CreateRectangle(v)))); curveList.AddRange(nodeCurves); var vectors = nodePositions.Select( (p, i) => new DebugCurve(220, 2, "red", new Polyline(p, newPositions[i]))).ToList(); foreach (Tuple<int, int, double, double> tuple in proximityEdgesWithDistance) { if (tuple.Item3 > 0) { curveList.Add(new DebugCurve(220, 1, "gray", new Polyline(nodePositions[tuple.Item1], nodePositions[tuple.Item2]))); } } curveList.AddRange(vectors); if (finalGridVectors != null) { var gridFlowVectors = nodePositions.Select((p, i) => new DebugCurve(220, 2, "blue", new Polyline(p, p + finalGridVectors[i]))) .ToList(); curveList.AddRange(gridFlowVectors); } LayoutAlgorithmSettings.ShowDebugCurves(curveList.ToArray()); } #endif }
Rectangle GetCommonRectangle(Size[] sizes, Point[] points) { var rect = Rectangle.CreateAnEmptyBox(); Debug.Assert(sizes.Length==points.Length); for (int i = 0; i < sizes.Length; i++) rect.Add(sizes[i], points[i]); return rect; }
/// <summary> /// /// </summary> /// <param name="nodeId1"></param> /// <param name="nodeId2"></param> /// <param name="point1"></param> /// <param name="point2"></param> /// <param name="nodeBoxes"></param> /// <param name="tRes"></param> /// <returns></returns> public static double GetIdealDistanceBetweenNodes(int nodeId1, int nodeId2, Point point1, Point point2, Size[] nodeBoxes, out double tRes) { if (nodeBoxes == null) throw new ArgumentNullException("nodeBoxes"); tRes = -1; if (nodeBoxes.Length <= nodeId1) return 0; if (nodeBoxes.Length <= nodeId2) return 0; const double expandMax = 1.5; const double expandMin = 1; // double tmax = 0; // double tmin = 1E10; const double machineAcc = 1.0e-16; double dist = (point1 - point2).Length; double dx = Math.Abs(point1.X - point2.X); double dy = Math.Abs(point1.Y - point2.Y); double wx = (nodeBoxes[nodeId1].Width/2 + nodeBoxes[nodeId2].Width/2); double wy = (nodeBoxes[nodeId1].Height/2 + nodeBoxes[nodeId2].Height/2); double t; if (dx < machineAcc*wx) { t = wy/dy; } else if (dy < machineAcc*wy) { t = wx/dx; } else { t = Math.Min(wx/dx, wy/dy); } if (t > 1) t = Math.Max(t, 1.001); // must be done, otherwise the convergence is very slow // tmax = Math.Max(tmax, t); // tmin = Math.Min(tmin, t); t = Math.Min(expandMax, t); t = Math.Max(expandMin, t); tRes = t; return t*dist; }
/// <summary> /// temporary due to merge problems. /// </summary> /// <param name="nodeId1"></param> /// <param name="nodeId2"></param> /// <param name="point1"></param> /// <param name="point2"></param> /// <param name="nodeBoxes"></param> /// <param name="tRes"></param> /// <returns></returns> public static double GetOverlapFactorBetweenNodes(int nodeId1, int nodeId2, Point point1, Point point2, Size[] nodeBoxes, out double tRes) { return GetIdealDistanceBetweenNodes(nodeId1, nodeId2, point1, point2, nodeBoxes, out tRes); }
/// <summary> /// Returns the ideal edge length, such that the overlap is removed. /// </summary> /// <param name="nodeId1"></param> /// <param name="nodeId2"></param> /// <param name="point1"></param> /// <param name="point2"></param> /// <param name="nodeBoxes"></param> /// <param name="tRes"></param> /// <returns></returns> static double GetIdealEdgeLength(int nodeId1, int nodeId2, Point point1, Point point2, Size[] nodeBoxes, out double tRes) { if (nodeBoxes == null) throw new ArgumentNullException("nodeBoxes"); // double expandMax = double.PositiveInfinity; //todo : this expands all the way const double expandMax = 1.5; const double expandMin = 1; //todo: replace machineAcc with global epsilon method in MSAGL const double machineAcc = 1.0e-16; double dist = (point1 - point2).Length; double dx = Math.Abs(point1.X - point2.X); double dy = Math.Abs(point1.Y - point2.Y); double wx = (nodeBoxes[nodeId1].Width/2 + nodeBoxes[nodeId2].Width/2); double wy = (nodeBoxes[nodeId1].Height/2 + nodeBoxes[nodeId2].Height/2); double t; if (dx < machineAcc*wx) { t = wy/dy; } else if (dy < machineAcc*wy) { t = wx/dx; } else { t = Math.Min(wx/dx, wy/dy); } if (t > 1) t = Math.Max(t, 1.001); // must be done, otherwise the convergence is very slow t = Math.Min(expandMax, t); t = Math.Max(expandMin, t); tRes = t; return t*dist; }
/// <summary> /// Shows the current state of the algorithm for debug purposes. /// </summary> /// <param name="treeEdges"></param> /// <param name="proximityEdges"></param> /// <param name="nodeSizes"></param> /// <param name="nodePos"></param> /// <param name="rootId"></param> void ShowAndMoveBoxesRemoveLater(List<Tuple<int, int, double, double, double>> treeEdges, List<Tuple<int, int, double, double, double>> proximityEdges, Size[] nodeSizes, Point[] nodePos, int rootId) { var l = new List<DebugCurve>(); foreach (var tuple in proximityEdges) l.Add(new DebugCurve(100, 0.5, "black", new LineSegment(nodePos[tuple.Item1], nodePos[tuple.Item2]))); //just for debug var nodeBoxes = new Rectangle[nodeSizes.Length]; for (int i = 0; i < nodePos.Length; i++) nodeBoxes[i] = new Rectangle(nodeSizes[i], nodePos[i]); l.AddRange(nodeBoxes.Select(b => new DebugCurve(100, 0.3, "green", b.Perimeter()))); if (treeEdges != null) l.AddRange( treeEdges.Select( e => new DebugCurve(200, GetEdgeWidth(e), "red", new LineSegment(nodePos[e.Item1], nodePos[e.Item2])))); if (rootId >= 0) l.Add(new DebugCurve(100, 10, "blue", CurveFactory.CreateOctagon(30, 30, nodePos[rootId]))); LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l); }
static Size[] GetNodeSizesByPaddingWithHalfSeparation(Node[] nodes, double nodeSeparation) { if (nodes == null) return null; var nodeSizes = new Size[nodes.Length]; var halfSep = nodeSeparation/2; for (int i = 0; i < nodes.Length; i++) { nodeSizes[i] = nodes[i].BoundingBox.Size; nodeSizes[i].Pad(halfSep); } return nodeSizes; }
public static void RemoveOverlapsForLayers(Node[] nodes, Size[] sizesOnLayers) { var settings = new OverlapRemovalSettings { RandomizeAllPointsOnStart = true, }; var mst = new OverlapRemoval(settings, nodes, sizesOnLayers); mst.RemoveOverlaps(); }
/// <summary> /// constructor with Size and center /// </summary> /// <param name="size"></param> /// <param name="center"></param> public Rectangle(Size size, Point center) { var w = size.Width/2; left = center.X - w; right = center.X + w; var h = size.Height / 2; bottom = center.Y - h; top = center.Y + h; }