internal void UpdateCostCache(Station node)
        {
            RectangleNode <CdtTriangle> cdtTree = cdt.GetCdtTree();

            node.CdtTriangle = cdtTree.FirstHitNode(node.Position, Test).UserData;

            node.cachedIdealRadius = HubRadiiCalculator.CalculateIdealHubRadiusWithNeighbors(metroGraphData, bundlingSettings, node);
            node.cachedRadiusCost  = costCalculator.RadiusCost(node, node.Position);
            node.cachedBundleCost  = 0;

            foreach (var adj in node.Neighbors)
            {
                if (!adj.IsRealNode)
                {
                    adj.cachedIdealRadius = HubRadiiCalculator.CalculateIdealHubRadiusWithNeighbors(metroGraphData, bundlingSettings, adj);
                    adj.cachedRadiusCost  = costCalculator.RadiusCost(adj, adj.Position);
                }

                StationEdgeInfo edgeInfo = metroGraphData.GetIjInfo(node, adj);
                adj.cachedBundleCost -= edgeInfo.cachedBundleCost;

                edgeInfo.cachedBundleCost = costCalculator.BundleCost(node, adj, node.Position);
                node.cachedBundleCost    += edgeInfo.cachedBundleCost;
                adj.cachedBundleCost     += edgeInfo.cachedBundleCost;
            }
        }
        internal double RadiusCost(Station node, Point newPosition)
        {
            double idealR;

            if (ApproximateComparer.Close(node.Position, newPosition))
            {
                idealR = node.cachedIdealRadius;
            }
            else
            {
                idealR = HubRadiiCalculator.CalculateIdealHubRadiusWithNeighbors(metroGraphData, bundlingSettings, node, newPosition);
            }


            List <Tuple <Polyline, Point> > touchedObstacles;

            if (!metroGraphData.looseIntersections.HubAvoidsObstacles(node, newPosition, idealR, out touchedObstacles))
            {
                return(Inf);
            }

            double cost = 0;

            foreach (var d in touchedObstacles)
            {
                double dist = (d.Item2 - newPosition).Length;
                cost += RError(idealR, dist, bundlingSettings);
            }

            return(cost);
        }
        double GetInkgain(PolylinePoint pp, Dictionary <PointPair, Set <Metroline> > segsToPolylines, Point a, Point b, Point c)
        {
            Set <Metroline> abPolylines, bcPolylines, abcPolylines;

            FindPolylines(pp, segsToPolylines, out abPolylines, out bcPolylines, out abcPolylines);
            double gain = 0;
            //ink
            double oldInk = ink;
            double newInk = ink;
            double ab     = (a - b).Length;
            double bc     = (b - c).Length;
            double ac     = (a - c).Length;

            if (abPolylines.Count == abcPolylines.Count)
            {
                newInk -= ab;
            }
            if (bcPolylines.Count == abcPolylines.Count)
            {
                newInk -= bc;
            }
            if (!segsToPolylines.ContainsKey(new PointPair(a, c)) || segsToPolylines[new PointPair(a, c)].Count == 0)
            {
                newInk += ac;
            }
            gain += CostCalculator.InkError(oldInk, newInk, bundlingSettings);

            //path lengths
            foreach (var metroline in abcPolylines)
            {
                double oldLength = polylineLength[metroline];
                double newLength = polylineLength[metroline];
                newLength -= ab + bc - ac;

                gain += CostCalculator.PathLengthsError(oldLength, newLength, metroline.IdealLength, bundlingSettings);
            }

            //radii
            double nowR     = GetCurrentHubRadius(metroGraphData.PointToStations[a]);
            double widthABC = metroGraphData.GetWidth(abcPolylines, bundlingSettings.EdgeSeparation);
            double widthABD = metroGraphData.GetWidth(abPolylines - abcPolylines, bundlingSettings.EdgeSeparation);
            double idealR   = HubRadiiCalculator.GetMinRadiusForTwoAdjacentBundles(nowR, a, c, b, widthABC, widthABD, metroGraphData, bundlingSettings);

            if (idealR > nowR)
            {
                gain -= CostCalculator.RError(idealR, nowR, bundlingSettings);
            }

            //check opposite side
            nowR = GetCurrentHubRadius(metroGraphData.PointToStations[c]);
            double widthCBD = metroGraphData.GetWidth(bcPolylines - abcPolylines, bundlingSettings.EdgeSeparation);

            idealR = HubRadiiCalculator.GetMinRadiusForTwoAdjacentBundles(nowR, c, b, a, widthCBD, widthABC, metroGraphData, bundlingSettings);
            if (idealR > nowR)
            {
                gain -= CostCalculator.RError(idealR, nowR, bundlingSettings);
            }

            return(gain);
        }
        double ComputeCostDeltaAfterEdgeGluing(Station node, Station a, Station b, Point newp)
        {
            double gain = 0;

            //ink
            double oldInk = metroGraphData.Ink;
            double newInk = metroGraphData.Ink - (node.Position - b.Position).Length - (node.Position - a.Position).Length +
                            (node.Position - newp).Length + (newp - a.Position).Length + (newp - b.Position).Length;

            gain += CostCalculator.InkError(oldInk, newInk, bundlingSettings);

            //path lengths
            foreach (var metroline in metroGraphData.GetIjInfo(node, b).Metrolines)
            {
                double oldLength = metroline.Length;
                double newLength = metroline.Length - (node.Position - b.Position).Length +
                                   (node.Position - newp).Length + (newp - b.Position).Length;
                gain += CostCalculator.PathLengthsError(oldLength, newLength, metroline.IdealLength, bundlingSettings);
            }
            foreach (var metroline in metroGraphData.GetIjInfo(node, a).Metrolines)
            {
                double oldLength = metroline.Length;
                double newLength = metroline.Length - (node.Position - a.Position).Length +
                                   (node.Position - newp).Length + (newp - a.Position).Length;
                gain += CostCalculator.PathLengthsError(oldLength, newLength, metroline.IdealLength, bundlingSettings);
            }

            //also compute radii gain
            //double nowR = Math.Min(GetCurrentHubRadius(node), (node.Position - newp).Length);
            //double id2 = HubRadiiCalculator.CalculateIdealHubRadiusWithNeighbors(metroGraphData, bundlingSettings, node);
            double id2    = node.cachedIdealRadius;
            double nowR   = GetCurrentHubRadius(node);
            double idealR = HubRadiiCalculator.GetMinRadiusForTwoAdjacentBundles(nowR, node, node.Position, a, b, metroGraphData, bundlingSettings);

            if (idealR > nowR)
            {
                gain += CostCalculator.RError(idealR, nowR, bundlingSettings);
            }

            if (id2 > (node.Position - newp).Length && !node.IsRealNode)
            {
                gain -= CostCalculator.RError(id2, (node.Position - newp).Length, bundlingSettings);
            }

            return(gain);
        }
 double GetCurrentHubRadius(Station node)
 {
     if (node.IsRealNode)
     {
         return(node.BoundaryCurve.BoundingBox.Diagonal / 2);
     }
     else
     {
         double idealR = node.cachedIdealRadius = HubRadiiCalculator.CalculateIdealHubRadiusWithNeighbors(this.metroGraphData, this.bundlingSettings, node);
         double r      = metroGraphData.looseIntersections.GetMinimalDistanceToObstacles(node, node.Position, idealR);
         Debug.Assert(r <= idealR);
         foreach (var adj in node.Neighbors)
         {
             r = Math.Min(r, (node.Position - adj.Position).Length);
         }
         return(r);
     }
 }
        internal void InitializeCostCache()
        {
            foreach (var v in metroGraphData.VirtualNodes())
            {
                v.cachedIdealRadius = HubRadiiCalculator.CalculateIdealHubRadiusWithNeighbors(metroGraphData, bundlingSettings, v);
                v.cachedRadiusCost  = costCalculator.RadiusCost(v, v.Position);
                v.cachedBundleCost  = 0;
            }

            foreach (var edge in metroGraphData.VirtualEdges())
            {
                var             v        = edge.Item1;
                var             u        = edge.Item2;
                StationEdgeInfo edgeInfo = metroGraphData.GetIjInfo(v, u);
                edgeInfo.cachedBundleCost = costCalculator.BundleCost(v, u, v.Position);
                v.cachedBundleCost       += edgeInfo.cachedBundleCost;
                u.cachedBundleCost       += edgeInfo.cachedBundleCost;
            }
        }
 IEnumerable <DebugCurve> IdealHubsWithNeighbors()
 {
     return(mgd.VirtualNodes().Select(station => new DebugCurve(200, 1, "black",
                                                                CurveFactory.CreateCircle(HubRadiiCalculator.CalculateIdealHubRadiusWithNeighbors(mgd, bundlingSettings, station), station.Position))));
 }