/// <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>
        /// apply a number of heuristics to improve current routing
        /// </summary>
        internal static void FixRouting(MetroGraphData metroGraphData, BundlingSettings bundlingSettings)
        {
// #if DEBUG && TEST_MSAGL
//             Debug.Assert(metroGraphData.looseIntersections.HubPositionsAreOK());
// #endif
            //TimeMeasurer.DebugOutput("Initial cost = " + CostCalculator.Cost(metroGraphData, bundlingSettings));
            //TimeMeasurer.DebugOutput("Initial cost of forces: " + CostCalculator.CostOfForces(metroGraphData, bundlingSettings));

            var adjuster = new NodePositionsAdjuster(metroGraphData, bundlingSettings);

            adjuster.GlueConflictingNodes();
            adjuster.UnglueEdgesFromBundleToSaveInk(true);

            var step     = 0;
            int MaxSteps = 10;

            while (++step < MaxSteps)
            {
/*#if DEBUG && TEST_MSAGL
 *              Debug.Assert(metroGraphData.looseIntersections.HubPositionsAreOK());
 #endif*/
                //heuristics to improve routing

                bool progress = adjuster.GlueConflictingNodes();

                progress |= adjuster.RelaxConstrainedEdges();

                progress |= (step <= 3 && adjuster.UnglueEdgesFromBundleToSaveInk(false));


                progress |= adjuster.GlueCollinearNeighbors(step);

                progress |= (step == 3 && adjuster.RemoveDoublePathCrossings());

                if (!progress)
                {
                    break;
                }
            }

            //one SA has to be executed with bundle forces
            metroGraphData.cdtIntersections.ComputeForcesForBundles = true;
            adjuster.RemoveDoublePathCrossings();
            adjuster.UnglueEdgesFromBundleToSaveInk(true);
            while (adjuster.GlueConflictingNodes())
            {
            }
            metroGraphData.Initialize(true); //this time initialize the tight enterables also

//            HubDebugger.ShowHubs(metroGraphData, bundlingSettings);
            //TimeMeasurer.DebugOutput("NodePositionsAdjuster stopped after " + step + " steps");
            //HubDebugger.ShowHubs(metroGraphData, bundlingSettings, true);

            //TimeMeasurer.DebugOutput("Final cost: " + CostCalculator.Cost(metroGraphData, bundlingSettings));
            //TimeMeasurer.DebugOutput("Final cost of forces: " + CostCalculator.CostOfForces(metroGraphData, bundlingSettings));
        }
        /// <summary>
        /// apply a number of heuristics to improve current routing
        /// </summary>
        internal static void FixRouting(MetroGraphData metroGraphData, BundlingSettings bundlingSettings) {
#if DEBUG && TEST_MSAGL
            Debug.Assert(metroGraphData.looseIntersections.HubPositionsAreOK());
#endif
            //TimeMeasurer.DebugOutput("Initial cost = " + CostCalculator.Cost(metroGraphData, bundlingSettings));
            //TimeMeasurer.DebugOutput("Initial cost of forces: " + CostCalculator.CostOfForces(metroGraphData, bundlingSettings));

            var adjuster = new NodePositionsAdjuster(metroGraphData, bundlingSettings);
            adjuster.GlueConflictingNodes();
            adjuster.UnglueEdgesFromBundleToSaveInk(true);

            var step = 0;
            int MaxSteps = 10;
            while (++step < MaxSteps) {
/*#if DEBUG && TEST_MSAGL
                Debug.Assert(metroGraphData.looseIntersections.HubPositionsAreOK());
#endif*/
                //heuristics to improve routing
                
                bool progress = adjuster.GlueConflictingNodes();

                progress |= adjuster.RelaxConstrainedEdges();

                progress |= (step <= 3 && adjuster.UnglueEdgesFromBundleToSaveInk(false));


                progress |= adjuster.GlueCollinearNeighbors(step);

                progress |= (step == 3 && adjuster.RemoveDoublePathCrossings());

                if (!progress) break;
            }

            //one SA has to be executed with bundle forces
            metroGraphData.cdtIntersections.ComputeForcesForBundles = true;
            adjuster.RemoveDoublePathCrossings();
            adjuster.UnglueEdgesFromBundleToSaveInk(true);
            while (adjuster.GlueConflictingNodes()) { }
            metroGraphData.Initialize(true); //this time initialize the tight enterables also

//            HubDebugger.ShowHubs(metroGraphData, bundlingSettings);
            //TimeMeasurer.DebugOutput("NodePositionsAdjuster stopped after " + step + " steps");
            //HubDebugger.ShowHubs(metroGraphData, bundlingSettings, true);

            //TimeMeasurer.DebugOutput("Final cost: " + CostCalculator.Cost(metroGraphData, bundlingSettings));
            //TimeMeasurer.DebugOutput("Final cost of forces: " + CostCalculator.CostOfForces(metroGraphData, bundlingSettings));
        }