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