private static IEnumerable <IEnumerable <Point> > RestorePathStages(List <WaypointEntry> waypointEntries, VertexEntry lastEntry)
        {
            // Back up to restore the path stages back to each waypoint.
            var paths = new List <IEnumerable <Point> >();

            waypointEntries.Reverse();
            foreach (var waypointEntry in waypointEntries)
            {
                paths.Add(SsstRectilinearPath.RestorePath(ref lastEntry, waypointEntry.waypointVector[0]));
            }

            // Add the first stage.
            paths.Add(SsstRectilinearPath.RestorePath(lastEntry));
            paths.Reverse();
            return(paths);
        }
Ejemplo n.º 2
0
/// <summary>
        /// Route a single stage of a possibly multi-stage (due to waypoints) path.
        /// </summary>
        /// <param name="sourceVertexEntries">The VertexEntry array that was in the source vertex if it was the target of a prior stage.</param>
        /// <param name="sources">The enumeration of source vertices; must be only one if sourceVertexEntries is non-null.</param>
        /// <param name="targets">The enumeration of target vertex entries; must be only one if targetVertexEntries is non-null.</param>
        /// <param name="targetVertexEntries">The VertexEntry array that is in the target at the end of the stage.</param>
        private VertexEntry GetPathStage(VertexEntry[] sourceVertexEntries, IEnumerable <VisibilityVertex> sources,
                                         VertexEntry[] targetVertexEntries, IEnumerable <VisibilityVertex> targets)
        {
            var         ssstCalculator = new SsstRectilinearPath();
            VertexEntry bestEntry      = null;

            // This contains the best (lowest) path cost after normalizing origins to the center of the sources
            // and targets.  This is used to avoid selecting a vertex pair whose path has more bends than another pair of
            // vertices, but the bend penalty didn't total enough to offset the additional length between the "better" pair.
            // This also plays the role of an upper bound on the path length; if a path cost is greater than adjustedMinCost
            // then we stop exploring it, which saves considerable time after low-cost paths have been found.
            double bestCost          = double.MaxValue / ScanSegment.OverlappedWeight;
            double bestPathCostRatio = double.PositiveInfinity;

            // Calculate the bend penalty multiplier.  This is a percentage of the distance between the source and target,
            // so that we have the same relative importance if we have objects of about size 20 that are about 100 apart
            // as for objects of about size 200 that are about 1000 apart.
            Point sourceCenter = Barycenter(sources);
            Point targetCenter = Barycenter(targets);
            var   distance     = SsstRectilinearPath.ManhattanDistance(sourceCenter, targetCenter);

            ssstCalculator.BendsImportance = Math.Max(0.001, distance * (this.bendPenaltyAsAPercentageOfDistance * 0.01));

            // We'll normalize by adding (a proportion of) the distance (only; not bends) from the current endpoints to
            // their centers. This is similar to routeToCenter, but routing multiple paths like this means we'll always
            // get at least a tie for the best vertex pair, whereas routeToCenter can introduce extraneous bends
            // if the sources/targets are not collinear with the center (such as an E-R diagram).
            // interiorLengthAdjustment is a way to decrease the cost adjustment slightly to allow a bend if it saves moving
            // a certain proportion of the distance parallel to the object before turning to it.
            var interiorLengthAdjustment = ssstCalculator.LengthImportance;

            // VertexEntries for the current pass of the current stage, if multistage.
            var tempTargetEntries = (targetVertexEntries != null) ? this.currentPassTargetEntries : null;

            // Process closest pairs first, so we can skip longer ones (jump out of SsstRectilinear sooner, often immediately).
            // This means that we'll be consistent on tiebreaking for equal scores with differing bend counts (the shorter
            // path will win).  In overlapped graphs the shortest path may have more higher-weight edges.
            foreach (var pair in
                     from VisibilityVertexRectilinear source in sources
                     from VisibilityVertexRectilinear target in targets
                     orderby SsstRectilinearPath.ManhattanDistance(source.Point, target.Point)
                     select new { sourceV = source, targetV = target })
            {
                var source = pair.sourceV;
                var target = pair.targetV;
                if (PointComparer.Equal(source.Point, target.Point))
                {
                    continue;
                }
                var sourceCostAdjustment = SsstRectilinearPath.ManhattanDistance(source.Point, sourceCenter) * interiorLengthAdjustment;
                var targetCostAdjustment = SsstRectilinearPath.ManhattanDistance(target.Point, targetCenter) * interiorLengthAdjustment;

                var adjustedBestCost = bestCost;
                if (targetVertexEntries != null)
                {
                    Array.Clear(tempTargetEntries, 0, tempTargetEntries.Length);
                    adjustedBestCost = ssstCalculator.MultistageAdjustedCostBound(bestCost);
                }
                VertexEntry lastEntry = ssstCalculator.GetPathWithCost(sourceVertexEntries, source, sourceCostAdjustment,
                                                                       tempTargetEntries, target, targetCostAdjustment,
                                                                       adjustedBestCost);
                if (tempTargetEntries != null)
                {
                    UpdateTargetEntriesForEachDirection(targetVertexEntries, tempTargetEntries, ref bestCost, ref bestEntry);
                    continue;
                }

                // This is the final (or only) stage. Break ties by picking the lowest ratio of cost to ManhattanDistance between the endpoints.
                if (lastEntry == null)
                {
                    continue;
                }
                var costRatio = lastEntry.Cost / SsstRectilinearPath.ManhattanDistance(source.Point, target.Point);
                if ((lastEntry.Cost < bestCost) || ApproximateComparer.Close(lastEntry.Cost, bestCost) && (costRatio < bestPathCostRatio))
                {
                    bestCost          = lastEntry.Cost;
                    bestEntry         = lastEntry;
                    bestPathCostRatio = lastEntry.Cost / SsstRectilinearPath.ManhattanDistance(source.Point, target.Point);
                }
            }
            return(bestEntry);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Get the lowest-cost path from one of one or more sources to one of one or more targets, without waypoints.
        /// </summary>
        /// <param name="sources">One or more source vertices</param>
        /// <param name="targets">One or more target vertices</param>
        /// <returns>A single enumeration of path points.</returns>
        internal IEnumerable <Point> GetPath(IEnumerable <VisibilityVertex> sources, IEnumerable <VisibilityVertex> targets)
        {
            var entry = GetPathStage(null, sources, null, targets);

            return(SsstRectilinearPath.RestorePath(entry));
        }
        /// <summary>
        /// Route a single stage of a possibly multi-stage (due to waypoints) path.
        /// </summary>
        /// <param name="sourceVertexEntries">The VertexEntry array that was in the source vertex if it was the target of a prior stage.</param>
        /// <param name="sources">The enumeration of source vertices; must be only one if sourceVertexEntries is non-null.</param>
        /// <param name="targets">The enumeration of target vertex entries; must be only one if targetVertexEntries is non-null.</param>
        /// <param name="targetVertexEntries">The VertexEntry array that is in the target at the end of the stage.</param>
        private VertexEntry GetPathStage(VertexEntry[] sourceVertexEntries, IEnumerable<VisibilityVertex> sources,
                                                VertexEntry[] targetVertexEntries, IEnumerable<VisibilityVertex> targets) {
            var ssstCalculator = new SsstRectilinearPath();
            VertexEntry bestEntry = null;

            // This contains the best (lowest) path cost after normalizing origins to the center of the sources
            // and targets.  This is used to avoid selecting a vertex pair whose path has more bends than another pair of
            // vertices, but the bend penalty didn't total enough to offset the additional length between the "better" pair.
            // This also plays the role of an upper bound on the path length; if a path cost is greater than adjustedMinCost 
            // then we stop exploring it, which saves considerable time after low-cost paths have been found.
            double bestCost = double.MaxValue / ScanSegment.OverlappedWeight;
            double bestPathCostRatio = double.PositiveInfinity;

            // Calculate the bend penalty multiplier.  This is a percentage of the distance between the source and target,
            // so that we have the same relative importance if we have objects of about size 20 that are about 100 apart
            // as for objects of about size 200 that are about 1000 apart.
            Point sourceCenter = GetBarycenterOfUniquePortLocations(sources);
            Point targetCenter = GetBarycenterOfUniquePortLocations(targets);
            var distance = SsstRectilinearPath.ManhattanDistance(sourceCenter, targetCenter);
            ssstCalculator.BendsImportance = Math.Max(0.001, distance * (this.bendPenaltyAsAPercentageOfDistance * 0.01));

            // We'll normalize by adding (a proportion of) the distance (only; not bends) from the current endpoints to
            // their centers. This is similar to routeToCenter, but routing multiple paths like this means we'll always
            // get at least a tie for the best vertex pair, whereas routeToCenter can introduce extraneous bends
            // if the sources/targets are not collinear with the center (such as an E-R diagram).
            // interiorLengthAdjustment is a way to decrease the cost adjustment slightly to allow a bend if it saves moving
            // a certain proportion of the distance parallel to the object before turning to it.
            var interiorLengthAdjustment = ssstCalculator.LengthImportance;

            // VertexEntries for the current pass of the current stage, if multistage.
            var tempTargetEntries = (targetVertexEntries != null) ? this.currentPassTargetEntries : null;

            // Process closest pairs first, so we can skip longer ones (jump out of SsstRectilinear sooner, often immediately).
            // This means that we'll be consistent on tiebreaking for equal scores with differing bend counts (the shorter
            // path will win).  In overlapped graphs the shortest path may have more higher-weight edges. 
            foreach (var pair in
                    from VisibilityVertexRectilinear source in sources
                    from VisibilityVertexRectilinear target in targets
                    orderby SsstRectilinearPath.ManhattanDistance(source.Point, target.Point)
                    select new { sourceV = source, targetV = target }) {
                var source = pair.sourceV;
                var target = pair.targetV;
                if (PointComparer.Equal(source.Point, target.Point)) {
                    continue;
                }
                var sourceCostAdjustment = SsstRectilinearPath.ManhattanDistance(source.Point, sourceCenter) * interiorLengthAdjustment;
                var targetCostAdjustment = SsstRectilinearPath.ManhattanDistance(target.Point, targetCenter) * interiorLengthAdjustment;

                var adjustedBestCost = bestCost;
                if (targetVertexEntries != null) {
                    Array.Clear(tempTargetEntries, 0, tempTargetEntries.Length);
                    adjustedBestCost = ssstCalculator.MultistageAdjustedCostBound(bestCost);
                }
                VertexEntry lastEntry = ssstCalculator.GetPathWithCost(sourceVertexEntries, source, sourceCostAdjustment,
                                                                        tempTargetEntries, target, targetCostAdjustment, 
                                                                        adjustedBestCost);
                if (tempTargetEntries != null) {
                    UpdateTargetEntriesForEachDirection(targetVertexEntries, tempTargetEntries, ref bestCost, ref bestEntry);
                    continue;
                }

                // This is the final (or only) stage. Break ties by picking the lowest ratio of cost to ManhattanDistance between the endpoints.
                if (lastEntry == null) {
                    continue;
                }
                var costRatio = lastEntry.Cost / SsstRectilinearPath.ManhattanDistance(source.Point, target.Point);
                if ((lastEntry.Cost < bestCost) || ApproximateComparer.Close(lastEntry.Cost, bestCost) && (costRatio < bestPathCostRatio)) {
                    bestCost = lastEntry.Cost;
                    bestEntry = lastEntry;
                    bestPathCostRatio = lastEntry.Cost / SsstRectilinearPath.ManhattanDistance(source.Point, target.Point);
                }
            }
            return bestEntry;
        }