using System; using System.Collections.Generic; using System.Linq; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Geometry.Curves; namespace Microsoft.Msagl.Routing.Rectilinear.Nudging { /// /// If two paths intersect then insert the intersection point as a vertex into both paths. /// Remove path self loops. Merge paths between the crossings if they have multiple crossings. /// If a path passes through a vertex of another path then insert this vertex into the first path. /// internal class PathRefiner { internal static void RefinePaths(IEnumerable paths, bool mergePaths) { AdjustPaths(paths); var pathsToFirstLinkedVertices = CreatePathsToFirstLinkedVerticesMap(paths); Refine(pathsToFirstLinkedVertices.Values); CrossVerticalAndHorizontalSegs(pathsToFirstLinkedVertices.Values); ReconstructPathsFromLinkedVertices(pathsToFirstLinkedVertices); if (mergePaths) new PathMerger(paths).MergePaths(); } /// /// make sure that every two different points in paths are separated by at least 10e-6 /// /// static void AdjustPaths(IEnumerable paths) { foreach (var path in paths) #if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369 path.PathPoints = AdjustPathPoints(path.PathPoints.Select(p=>p.Clone()).ToArray()).ToArray(); #else path.PathPoints = AdjustPathPoints(path.PathPoints as IList).ToArray(); #endif } static IEnumerable AdjustPathPoints(IList points) { Point p = AdjustPoint(points[0]); yield return p; for(int i=1;i pathsFirstLinked) { var horizontalPoints = new List(); var verticalPoints = new List(); foreach (var pnt in pathsFirstLinked) for (var p = pnt; p.Next != null; p = p.Next) if (ApproximateComparer.Close(p.Point.X, p.Next.Point.X)) verticalPoints.Add(p); else horizontalPoints.Add(p); (new LinkedPointSplitter(horizontalPoints, verticalPoints)).SplitPoints(); } static void ReconstructPathsFromLinkedVertices(Dictionary pathsToPathLinkedPoints) { foreach (var pair in pathsToPathLinkedPoints) pair.Key.PathPoints = pair.Value; } static void Refine(IEnumerable pathFirstPoints) { RefineInDirection(Directions.North, pathFirstPoints); RefineInDirection(Directions.East, pathFirstPoints); } /// /// refines all segments that are parallel to "direction" /// /// /// static void RefineInDirection(Directions direction, IEnumerable pathFirstPoints) { PointProjection projectionToDirection, projectionToPerp; GetProjectionsDelegates(direction, out projectionToPerp, out projectionToDirection); var linkedPointsInDirection = GetAllLinkedVertsInDirection(projectionToPerp, pathFirstPoints).ToArray(); var colliniarBuckets = linkedPointsInDirection.GroupBy(p => projectionToPerp(p.Point)); foreach (var pathLinkedPointBucket in colliniarBuckets) RefineCollinearBucket(pathLinkedPointBucket, projectionToDirection); } static void GetProjectionsDelegates(Directions direction, out PointProjection projectionToPerp, out PointProjection projectionToDirection) { if (direction == Directions.East) { projectionToDirection = delegate(Point p) { return p.X; }; projectionToPerp = delegate(Point p) { return p.Y; }; } else { projectionToPerp = delegate(Point p) { return p.X; }; projectionToDirection = delegate(Point p) { return p.Y; }; } } static IEnumerable GetAllLinkedVertsInDirection( PointProjection projectionToPerp, IEnumerable initialVerts) { foreach (var vert in initialVerts) for (var v = vert; v.Next != null; v = v.Next) if ( ApproximateComparer.Close(projectionToPerp(v.Point),projectionToPerp(v.Next.Point))) yield return v; } /// /// refine vertices belonging to a bucket; /// pathLinkedVertices belong to a line parallel to the direction of the refinement /// /// /// static void RefineCollinearBucket( IEnumerable pathLinkedVertices, PointProjection projectionToDirection) { var dict = new SortedDictionary(new PointByDelegateComparer(projectionToDirection)); foreach (var pathLinkedPoint in pathLinkedVertices) { if (!dict.ContainsKey(pathLinkedPoint.Point)) dict[pathLinkedPoint.Point] = 0; if (!dict.ContainsKey(pathLinkedPoint.Next.Point)) dict[pathLinkedPoint.Next.Point] = 0; } var arrayOfPoints = new Point[dict.Count]; int i = 0; foreach (var point in dict.Keys) arrayOfPoints[i++] = point; for (i = 0; i < arrayOfPoints.Length; i++) dict[arrayOfPoints[i]] = i; foreach (var pathLinkedVertex in pathLinkedVertices) { i = dict[pathLinkedVertex.Point]; int j = dict[pathLinkedVertex.Next.Point]; if (Math.Abs(j - i) > 1) InsertPoints(pathLinkedVertex, arrayOfPoints, i, j); } } static void InsertPoints(LinkedPoint pathLinkedVertex, Point[] arrayOfPoints, int i, int j) { if (i < j) pathLinkedVertex.InsertVerts(i, j, arrayOfPoints); else pathLinkedVertex.InsertVertsInReverse(j, i, arrayOfPoints); } static Dictionary CreatePathsToFirstLinkedVerticesMap(IEnumerable edgePaths) { var dict = new Dictionary(); foreach (var path in edgePaths) dict[path] = CreateLinkedVertexOfEdgePath(path); return dict; } static LinkedPoint CreateLinkedVertexOfEdgePath(Path path) { var pathPoint = new LinkedPoint(path.PathPoints.First()); var first = pathPoint; #if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=368 path.PathPoints.Skip(1).Aggregate(pathPoint, (lp, p) => { lp.Next = new LinkedPoint(p); return lp.Next; }); #else path.PathPoints.Skip(1).Aggregate(pathPoint, (lp, p) => lp.Next = new LinkedPoint(p)); #endif return first; } } }