using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Msagl.Core.Geometry; namespace Microsoft.Msagl.Routing.Rectilinear.Nudging { /// ///Avoid a situation where two paths cross each other more than once. Remove self loops. /// /// internal class PathMerger{ internal PathMerger(IEnumerable paths){ Paths = paths; } internal IEnumerable Paths { get; set; } readonly Dictionary> verticesToPathOffsets = new Dictionary>(); /// /// Avoid a situation where two paths cross each other more than once. Remove self loops. /// internal void MergePaths(){ InitVerticesToPathOffsetsAndRemoveSelfCycles(); foreach (var path in Paths) ProcessPath(path); } void ProcessPath(Path path) { var departedPaths = new Dictionary(); Dictionary prevLocationPathOffsets = null; for (var linkedPoint = (LinkedPoint) path.PathPoints; linkedPoint != null; linkedPoint = linkedPoint.Next) { var pathOffsets = verticesToPathOffsets[linkedPoint.Point]; if (prevLocationPathOffsets != null) { //handle returning paths if (departedPaths.Count > 0) foreach (var pair in pathOffsets) { LinkedPoint departerLinkedPoint; var path0 = pair.Key; if (departedPaths.TryGetValue(path0, out departerLinkedPoint)) {//returned! CollapseLoopingPath(path0, departerLinkedPoint, pair.Value, path, linkedPoint); departedPaths.Remove(path0); } } //find departed paths foreach (var pair in prevLocationPathOffsets.Where(pair => !pathOffsets.ContainsKey(pair.Key))) departedPaths.Add(pair.Key, pair.Value); } prevLocationPathOffsets = pathOffsets; } } // bool Correct() { // foreach (var kv in verticesToPathOffsets) { // Point p = kv.Key; // Dictionary pathOffs = kv.Value; // foreach (var pathOff in pathOffs) { // var path = pathOff.Key; // var linkedPoint = pathOff.Value; // if (linkedPoint.Point != p) // return false; // if (FindLinkedPointInPath(path, p) == null) { // return false; // } // } // } // return true; // } void CollapseLoopingPath(Path loopingPath, LinkedPoint departureFromLooping, LinkedPoint arrivalToLooping, Path stemPath, LinkedPoint arrivalToStem){ var departurePointOnStem = FindLinkedPointInPath(stemPath, departureFromLooping.Point); IEnumerable pointsToInsert = GetPointsInBetween(departurePointOnStem, arrivalToStem); if(Before(departureFromLooping, arrivalToLooping)){ CleanDisappearedPiece(departureFromLooping, arrivalToLooping, loopingPath); ReplacePiece(departureFromLooping, arrivalToLooping, pointsToInsert,loopingPath); }else{ CleanDisappearedPiece(arrivalToLooping, departureFromLooping, loopingPath); ReplacePiece(arrivalToLooping, departureFromLooping, pointsToInsert.Reverse(),loopingPath); } } static IEnumerable GetPointsInBetween(LinkedPoint a, LinkedPoint b){ for(var i=a.Next;i!=b;i=i.Next) yield return i.Point; } void ReplacePiece(LinkedPoint a, LinkedPoint b, IEnumerable points, Path loopingPath){ var prevPoint = a; foreach (var point in points){ var lp = new LinkedPoint(point); prevPoint.Next = lp; prevPoint = lp; var pathOffset = verticesToPathOffsets[point]; Debug.Assert(!pathOffset.ContainsKey(loopingPath)); pathOffset[loopingPath] = prevPoint; } prevPoint.Next = b; } void CleanDisappearedPiece(LinkedPoint a, LinkedPoint b, Path loopingPath){ foreach (var point in GetPointsInBetween(a, b)) { var pathOffset = verticesToPathOffsets[point]; Debug.Assert(pathOffset.ContainsKey(loopingPath)); pathOffset.Remove(loopingPath); } } /// /// checks that a is before b in the path /// /// /// /// true is a is before b in the path static bool Before(LinkedPoint a, LinkedPoint b){ for(a=a.Next;a!=null;a=a.Next) if(a==b) return true; return false; } static LinkedPoint FindLinkedPointInPath(Path path, Point point) { //this function is supposed to always succeed. it will throw a null reference exception otherwise for (var linkedPoint = (LinkedPoint) path.PathPoints;; linkedPoint = linkedPoint.Next) if (linkedPoint.Point == point) return linkedPoint; } void InitVerticesToPathOffsetsAndRemoveSelfCycles(){ foreach(var path in Paths){ for (var linkedPoint = (LinkedPoint)path.PathPoints; linkedPoint != null; linkedPoint = linkedPoint.Next) { Dictionary pathOffsets; if (!verticesToPathOffsets.TryGetValue(linkedPoint.Point, out pathOffsets)) verticesToPathOffsets[linkedPoint.Point] = pathOffsets = new Dictionary(); //check for the loop LinkedPoint loopPoint; if (pathOffsets.TryGetValue(path, out loopPoint)) {//we have a loop CleanDisappearedPiece(loopPoint, linkedPoint, path); loopPoint.Next = linkedPoint.Next; } else pathOffsets[path] = linkedPoint; } } } } }