/// <summary>
        /// switch flips
        /// </summary>
        bool RemoveDoublePathCrossings()
        {
            bool progress = new PathFixer(metroGraphData, metroGraphData.PointIsAcceptableForEdge).Run();

            if (progress)
            {
                metroGraphData.Initialize(false);
                SimulatedAnnealing.FixRouting(metroGraphData, bundlingSettings);
            }

            return(progress);
        }
        /// <summary>
        /// unite the nodes that are close to each other
        /// </summary>
        bool GlueConflictingNodes()
        {
            var circlesHierarchy = GetCirclesHierarchy();

            if (circlesHierarchy == null)
            {
                return(false);
            }
            var gluingMap   = new Dictionary <Station, Station>();
            var gluedDomain = new Set <Station>();

            RectangleNodeUtils.CrossRectangleNodes <Station>(circlesHierarchy, circlesHierarchy,
                                                             (i, j) => TryToGlueNodes(i, j, gluingMap, gluedDomain));
            if (gluingMap.Count == 0)
            {
                return(false);
            }

            for (int i = 0; i < metroGraphData.Edges.Length; i++)
            {
                RegenerateEdge(gluingMap, i);
            }

            //can it be more efficient?
            HashSet <Point> affectedPoints = new HashSet <Point>();

            foreach (var s in gluedDomain)
            {
                affectedPoints.Add(s.Position);
                foreach (var neig in s.Neighbors)
                {
                    if (!neig.IsRealNode)
                    {
                        affectedPoints.Add(neig.Position);
                    }
                }
            }

            //TimeMeasurer.DebugOutput("gluing nodes");
            metroGraphData.Initialize(false);

            SimulatedAnnealing.FixRouting(metroGraphData, bundlingSettings, affectedPoints);
            return(true);
        }
        /// <summary>
        /// Unbundle unnecessary edges:
        ///  instead of one bundle (a->bcd) we get two bundles (a->b,a->cd) with smaller ink
        /// </summary>
        bool UnglueEdgesFromBundleToSaveInk(bool alwaysExecuteSA)
        {
            var segsToPolylines = new Dictionary <PointPair, Set <Metroline> >();

            ink            = metroGraphData.Ink;
            polylineLength = new Dictionary <Metroline, double>();
            //create polylines
            foreach (var metroline in metroGraphData.Metrolines)
            {
                polylineLength[metroline] = metroline.Length;
                for (var pp = metroline.Polyline.StartPoint; pp.Next != null; pp = pp.Next)
                {
                    var segment = new PointPair(pp.Point, pp.Next.Point);
                    CollectionUtilities.AddToMap(segsToPolylines, segment, metroline);
                }
            }
            var affectedPoints = new HashSet <Point>();
            var progress       = false;

            foreach (var metroline in metroGraphData.Metrolines)
            {
                var obstaclesAllowedToIntersect =
                    metroGraphData.PointToStations[metroline.Polyline.Start].EnterableLoosePolylines *
                    metroGraphData.PointToStations[metroline.Polyline.End].EnterableLoosePolylines;
                if (TrySeparateOnPolyline(metroline, segsToPolylines, affectedPoints, obstaclesAllowedToIntersect))
                {
                    progress = true;
                }
            }

            if (progress) //TimeMeasurer.DebugOutput("unbundling");
            {
                metroGraphData.Initialize(false);
            }

            if (alwaysExecuteSA || progress)
            {
                SimulatedAnnealing.FixRouting(metroGraphData, bundlingSettings, alwaysExecuteSA ? null : affectedPoints);
            }

            return(progress);
        }
        /// <summary>
        /// Fix the situation where a station has two neighbors that are almost in the same directions
        /// </summary>
        bool GlueCollinearNeighbors(int step)
        {
            HashSet <Point> affectedPoints = new HashSet <Point>();
            bool            progress       = false;

            foreach (var node in metroGraphData.Stations)
            {
                if (GlueCollinearNeighbors(node, affectedPoints, step))
                {
                    progress = true;
                }
            }

            if (progress)
            {
                //TimeMeasurer.DebugOutput("gluing edges");
                metroGraphData.Initialize(false);
                SimulatedAnnealing.FixRouting(metroGraphData, bundlingSettings, affectedPoints);
            }

            return(progress);
        }
        /// <summary>
        /// split each edge that is too much constrained by the obstacles
        /// </summary>
        bool RelaxConstrainedEdges()
        {
            HashSet <Point> affectedPoints = new HashSet <Point>();
            bool            progress       = false;

            foreach (var edge in metroGraphData.VirtualEdges())
            {
                if (RelaxConstrainedEdge(edge.Item1, edge.Item2, affectedPoints))
                {
                    progress = true;
                }
            }

            if (progress)
            {
                //TimeMeasurer.DebugOutput("relaxing constrained edges");
                metroGraphData.Initialize(false);

                SimulatedAnnealing.FixRouting(metroGraphData, bundlingSettings, affectedPoints);
            }

            return(progress);
        }