import {GenericBinaryHeapPriorityQueue} from '../structs/genericBinaryHeapPriorityQueue' import {TollFreeVisibilityEdge} from './visibility/TollFreeVisibilityEdge' import {VisibilityEdge} from './visibility/VisibilityEdge' import {VisibilityGraph} from './visibility/VisibilityGraph' import {VisibilityVertex} from './visibility/VisibilityVertex' export class MultipleSourceMultipleTargetsShortestPathOnVisibilityGraph { // we are not using the A* algorithm since it does not make much sense for muliple targets // but we use the upper bound heuristic sources: Array targets: Set _current: VisibilityVertex closestTarget: VisibilityVertex upperBound: number = Number.POSITIVE_INFINITY _visGraph: VisibilityGraph constructor(sourceVisVertices: Array, targetVisVertices: Array, visibilityGraph: VisibilityGraph) { this._visGraph = visibilityGraph visibilityGraph.ClearPrevEdgesTable() for (const v of visibilityGraph.Vertices()) v.Distance = Number.POSITIVE_INFINITY this.sources = sourceVisVertices this.targets = new Set(targetVisVertices) } // Returns a path GetPath(): Array { const pq = new GenericBinaryHeapPriorityQueue() for (const v of this.sources) { v.Distance = 0 pq.Enqueue(v, 0) } while (!pq.IsEmpty()) { this._current = pq.Dequeue() if (this.targets.has(this._current)) break for (const e of this._current.OutEdges) if (this.PassableOutEdge(e)) this.ProcessNeighbor(pq, e, e.Target) for (const e of this._current.InEdges.filter(this.PassableInEdge.bind)) this.ProcessNeighbor(pq, e, e.Source) } return this._visGraph.PreviosVertex(this._current) == null ? null : this.CalculatePath() } PassableOutEdge(e: VisibilityEdge): boolean { return this.targets.has(e.Target) || !MultipleSourceMultipleTargetsShortestPathOnVisibilityGraph.IsForbidden(e) } PassableInEdge(e: VisibilityEdge): boolean { return this.targets.has(e.Source) || !MultipleSourceMultipleTargetsShortestPathOnVisibilityGraph.IsForbidden(e) } static IsForbidden(e: VisibilityEdge): boolean { return ((e.IsPassable != null && !e.IsPassable()) || e) instanceof TollFreeVisibilityEdge } ProcessNeighbor(pq: GenericBinaryHeapPriorityQueue, l: VisibilityEdge, v: VisibilityVertex) { const len = l.Length const c = this._current.Distance + len if (c >= this.upperBound) { return } if (this.targets.has(v)) { this.upperBound = c this.closestTarget = v } if (this._visGraph.PreviosVertex(v) == null) { v.Distance = c this._visGraph.SetPreviousEdge(v, l) pq.Enqueue(v, c) } else if (c < v.Distance) { // This condition should never hold for the dequeued nodes. // However because of a very rare case of an epsilon error it might! // In this case DecreasePriority will fail to find "v" and the algorithm will continue working. // Since v is not in the queue changing its .Distance will not mess up the queue. // Changing v.Prev is fine since we come up with a path with an insignificantly // smaller distance. v.Distance = c this._visGraph.SetPreviousEdge(v, l) pq.DecreasePriority(v, c) } } CalculatePath(): Array { if (this.closestTarget == null) { return null } const ret = new Array() let v = this.closestTarget do { ret.push(v) v = this._visGraph.PreviosVertex(v) } while (v.Distance > 0) ret.push(v) return ret.reverse() } }