using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Msagl.Core.DataStructures;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.DebugHelpers;
namespace Microsoft.Msagl.Routing.Spline.Bundling {
///
/// Linear algorithm as described in our paper
/// Edge Routing with Ordered Bunldles
///
public class LinearMetroMapOrdering : IMetroMapOrderingAlgorithm {
///
/// bundle lines
///
readonly List MetrolinesGlobal;
List Metrolines;
///
/// Station positions
///
Point[] positions;
///
/// Initialize bundle graph and build the ordering
///
internal LinearMetroMapOrdering(List MetrolinesGlobal, Dictionary pointToIndex) {
this.MetrolinesGlobal = MetrolinesGlobal;
ConvertParameters(pointToIndex);
BuildOrder();
}
///
/// Get the ordering of lines on station u with respect to the edge (u->v)
///
IEnumerable IMetroMapOrderingAlgorithm.GetOrder(Station u, Station v) {
MetroEdge me = MetroEdge.CreateFromTwoNodes(u.SerialNumber, v.SerialNumber);
List orderedMetrolineListForUv = order[me];
if (u.SerialNumber < v.SerialNumber) {
foreach (int MetrolineIndex in orderedMetrolineListForUv)
yield return MetrolinesGlobal[MetrolineIndex];
}
else {
for (int i = orderedMetrolineListForUv.Count - 1; i >= 0; i--)
yield return MetrolinesGlobal[orderedMetrolineListForUv[i]];
}
}
///
/// Get the index of line on the edge (u->v) and node u
///
int IMetroMapOrderingAlgorithm.GetLineIndexInOrder(Station u, Station v, Metroline Metroline) {
MetroEdge me = MetroEdge.CreateFromTwoNodes(u.SerialNumber, v.SerialNumber);
Dictionary d = lineIndexInOrder[me];
if (u.SerialNumber < v.SerialNumber) {
return d[Metroline];
}
else {
return d.Count - 1 - d[Metroline];
}
}
void ConvertParameters(Dictionary pointToIndex) {
Metrolines = new List();
positions = new Point[pointToIndex.Count];
foreach (Metroline gline in MetrolinesGlobal) {
List line = new List();
foreach (Point p in gline.Polyline) {
line.Add(pointToIndex[p].SerialNumber);
positions[pointToIndex[p].SerialNumber] = p;
}
Metrolines.Add(line.ToArray());
}
}
//order for node u of edge u->v
Dictionary> order;
Dictionary> lineIndexInOrder;
HashSet nonTerminals;
HashSet initialEdges;
///
/// Edge in graph H
/// label is used to distinguish multiple edges
///
class MetroEdge {
List nodes;
internal static MetroEdge CreateFromTwoNodes(int u, int v) {
MetroEdge res = new MetroEdge();
res.nodes = new List();
res.nodes.Add(Math.Min(u, v));
res.nodes.Add(Math.Max(u, v));
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=289
res.UpdateHashKey();
#endif
return res;
}
internal static MetroEdge CreateFromTwoEdges(int v, MetroEdge e1, MetroEdge e2) {
int s = e1.Source() == v ? e1.Target() : e1.Source();
int t = e2.Source() == v ? e2.Target() : e2.Source();
if (s < t)
return CreateFromTwoEdges(v, e1.nodes, e2.nodes);
else
return CreateFromTwoEdges(v, e2.nodes, e1.nodes);
}
internal static MetroEdge CreateFromTwoEdges(int v, List e1, List e2) {
List nodes = new List(e1.Count + e2.Count - 1);
if (e1[0] != v) {
for (int i = 0; i < e1.Count; i++)
nodes.Add(e1[i]);
}
else {
for (int i = e1.Count - 1; i >= 0; i--)
nodes.Add(e1[i]);
}
if (e2[0] == v) {
for (int i = 1; i < e2.Count; i++)
nodes.Add(e2[i]);
}
else {
for (int i = e2.Count - 2; i >= 0; i--)
nodes.Add(e2[i]);
}
MetroEdge res = new MetroEdge();
res.nodes = nodes;
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=289
res.UpdateHashKey();
#endif
return res;
}
internal int Source() {
return nodes[0];
}
internal int Target() {
return nodes[nodes.Count - 1];
}
public override string ToString() {
string s = "(";
foreach (int i in nodes)
s += i + " ";
s += ")";
return s;
}
int label;
bool labelCached = false;
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=289 Support Dictionary directly based on object's GetHashCode
private SharpKit.JavaScript.JsString _hashKey;
private void UpdateHashKey()
{
_hashKey = GetHashCode().ToString();
}
#endif
public override int GetHashCode()
{
if (!labelCached) {
ulong hc = (ulong)nodes.Count;
for (int i = 0; i < nodes.Count; i++) {
hc = unchecked(hc * 314159 + (ulong)nodes[i]);
}
label = (int)hc;
labelCached = true;
}
return label;
}
///
/// overrides the equality
///
///
///
public override bool Equals(object obj) {
if (!(obj is MetroEdge))
return false;
return (MetroEdge)obj == this;
}
public static bool operator ==(MetroEdge pair0, MetroEdge pair1) {
if (pair0.GetHashCode() != pair1.GetHashCode()) return false;
return true;
//TODO: are conflicts possible?
//return pair0.nodes.SequenceEqual(pair1.nodes);
}
public static bool operator !=(MetroEdge pair0, MetroEdge pair1) {
return !(pair0 == pair1);
}
}
///
/// unordered list of paths on a specified edge
///
class PathList {
internal MetroEdge edge;
internal HashSet paths;
internal List subLists;
public override string ToString() {
return edge.ToString() + " (" + paths.Count + ")";
}
}
class PathOnEdge {
internal int index;
internal LinkedListNode node;
public override string ToString() {
string s = "(index = " + index + ")";
return s;
}
}
Dictionary> orderedAdjacent;
Dictionary, LinkedListNode> adjacencyIndex;
Dictionary e2p;
Dictionary> paths;
///
/// Do the main job
///
void BuildOrder() {
//init local structures
Initialize();
//ordering itself
foreach (int v in nonTerminals) {
ProcessNonTerminal(v);
}
//get result
RestoreResult();
}
void Initialize() {
//non terminals and adjacent
nonTerminals = new HashSet();
initialEdges = new HashSet();
//non-sorted adjacent edges. will be sorted later
Dictionary> adjacent = new Dictionary>();
for (int mi = 0; mi < Metrolines.Count; mi++) {
int[] Metroline = Metrolines[mi];
for (int i = 0; i + 1 < Metroline.Length; i++) {
MetroEdge me = MetroEdge.CreateFromTwoNodes(Metroline[i], Metroline[i + 1]);
if (!initialEdges.Contains(me))
initialEdges.Add(me);
if (i + 2 < Metroline.Length)
nonTerminals.Add(Metroline[i + 1]);
CollectionUtilities.AddToMap(adjacent, Metroline[i], me);
CollectionUtilities.AddToMap(adjacent, Metroline[i + 1], me);
}
}
//order neighbors around each vertex
InitAdjacencyData(adjacent);
//create e2p and paths...
InitPathData();
}
void InitPathData() {
paths = new Dictionary>();
e2p = new Dictionary();
for (int mi = 0; mi < Metrolines.Count; mi++) {
int[] Metroline = Metrolines[mi];
paths.Add(mi, new LinkedList());
for (int i = 0; i + 1 < Metroline.Length; i++) {
MetroEdge me = MetroEdge.CreateFromTwoNodes(Metroline[i], Metroline[i + 1]);
if (!e2p.ContainsKey(me)) {
PathList pl = new PathList();
pl.edge = me;
pl.paths = new HashSet();
e2p.Add(me, pl);
}
PathOnEdge pathOnEdge = new PathOnEdge();
pathOnEdge.index = mi;
pathOnEdge.node = paths[mi].AddLast(me);
e2p[me].paths.Add(pathOnEdge);
}
}
}
void InitAdjacencyData(Dictionary> adjacent) {
orderedAdjacent = new Dictionary>();
adjacencyIndex = new Dictionary, LinkedListNode>();
foreach (int v in adjacent.Keys) {
List adj = new List(adjacent[v]);
orderedAdjacent.Add(v, SortAdjacentEdges(v, adj));
}
}
LinkedList SortAdjacentEdges(int v, List adjacent) {
MetroEdge mn = adjacent.First();
int mnv = OppositeNode(mn, v);
adjacent.Sort(delegate(MetroEdge edge1, MetroEdge edge2) {
int a = OppositeNode(edge1, v);
int b = OppositeNode(edge2, v);
//TODO: remove angles!
double angA = Point.Angle(positions[a] - positions[v], positions[mnv] - positions[v]);
double angB = Point.Angle(positions[b] - positions[v], positions[mnv] - positions[v]);
return angA.CompareTo(angB);
});
LinkedList res = new LinkedList();
foreach (MetroEdge edge in adjacent) {
LinkedListNode node = res.AddLast(edge);
adjacencyIndex.Add(new Tuple(v, edge), node);
}
return res;
}
///
/// update adjacencies of node 'a': put new edges instead of oldEdge
///
void UpdateAdjacencyData(int a, MetroEdge oldEdge, List newSubList) {
//find a (cached) position of oldEdge in order
LinkedListNode node = adjacencyIndex[new Tuple(a, oldEdge)];
Debug.Assert(node.Value == oldEdge);
LinkedListNode inode = node;
foreach (PathList pl in newSubList) {
MetroEdge newEdge = pl.edge;
if (oldEdge.Source() == a)
node = node.List.AddAfter(node, newEdge);
else
node = node.List.AddBefore(node, newEdge);
adjacencyIndex.Add(new Tuple(a, newEdge), node);
}
adjacencyIndex.Remove(new Tuple(a, oldEdge));
inode.List.Remove(inode);
}
///
/// recursively build an order on the edge
///
List RestoreResult(MetroEdge edge) {
List res = new List();
PathList pl = e2p[edge];
if (pl.subLists == null) {
foreach (PathOnEdge path in pl.paths)
res.Add(path.index);
}
else {
foreach (PathList subList in pl.subLists) {
List subResult = RestoreResult(subList.edge);
if (!(edge.Source() == subList.edge.Source() || edge.Target() == subList.edge.Target()))
subResult.Reverse();
res.AddRange(subResult);
}
}
return res;
}
void RestoreResult() {
order = new Dictionary>();
lineIndexInOrder = new Dictionary>();
foreach (MetroEdge me in initialEdges) {
order.Add(me, RestoreResult(me));
Dictionary d = new Dictionary();
int index = 0;
foreach (int v in order[me]) {
d[MetrolinesGlobal[v]] = index++;
}
lineIndexInOrder.Add(me, d);
}
}
///
/// Remove vertex v from the graph. Update graph and paths correspondingly
///
void ProcessNonTerminal(int v) {
//oldEdge => sorted PathLists
Dictionary> newSubLists = RadixSort(v);
//update current data
foreach (MetroEdge oldEdge in orderedAdjacent[v]) {
Debug.Assert(e2p.ContainsKey(oldEdge));
List newSubList = newSubLists[oldEdge];
//update e2p[oldEdge]
e2p[oldEdge].paths = null;
e2p[oldEdge].subLists = newSubList;
//update ordered adjacency data
UpdateAdjacencyData(OppositeNode(oldEdge, v), oldEdge, newSubList);
//update paths and add new edges
foreach (PathList pl in newSubList) {
MetroEdge newEdge = pl.edge;
//we could check the reverse edge before
if (e2p.ContainsKey(newEdge)) continue;
//add e2p for new edge
e2p.Add(newEdge, pl);
//update paths
foreach (PathOnEdge path in pl.paths) {
UpdatePath(path, v, newEdge);
}
}
}
}
///
/// Linear sorting of paths passing through vertex v
///
Dictionary> RadixSort(int v) {
//build a map [old_edge => list_of_paths_on_it]; the relative order of paths is important
Dictionary> r = new Dictionary>();
//first index in circular order
Dictionary firstIndex = new Dictionary();
foreach (MetroEdge oldEdge in orderedAdjacent[v]) {
PathList pathList = e2p[oldEdge];
foreach (PathOnEdge path in pathList.paths) {
MetroEdge ej = FindNextEdgeOnPath(v, path);
CollectionUtilities.AddToMap(r, ej, path);
}
firstIndex.Add(oldEdge, (r.ContainsKey(oldEdge) ? r[oldEdge].Count : 0));
}
//oldEdge => SortedPathLists
Dictionary> res = new Dictionary>();
//build the desired order for each edge
foreach (MetroEdge oldEdge in orderedAdjacent[v]) {
//r[oldEdge] is the right order! (up to the circleness)
List paths = r[oldEdge];
Debug.Assert(paths.Count > 0);
List subLists = new List();
HashSet curPathSet = new HashSet();
for (int j = 0; j < paths.Count; j++) {
int i = (j + firstIndex[oldEdge]) % paths.Count;
MetroEdge nowEdge = paths[i].node.Value;
MetroEdge nextEdge = paths[(i + 1) % paths.Count].node.Value;
curPathSet.Add(paths[i]);
if (j == paths.Count - 1 || nowEdge != nextEdge) {
//process
MetroEdge newEdge = MetroEdge.CreateFromTwoEdges(v, oldEdge, nowEdge);
PathList pl = new PathList();
pl.edge = newEdge;
pl.paths = curPathSet;
subLists.Add(pl);
//clear
curPathSet = new HashSet();
}
}
if (oldEdge.Source() == v) subLists.Reverse();
res.Add(oldEdge, subLists);
}
return res;
}
///
/// extract the next edge on a given path after node v
///
MetroEdge FindNextEdgeOnPath(int v, PathOnEdge pathOnEdge) {
if (pathOnEdge.node.Next != null) {
int o = OppositeNode(pathOnEdge.node.Next.Value, v);
if (o != -1) return pathOnEdge.node.Next.Value;
}
if (pathOnEdge.node.Previous != null) {
int o = OppositeNode(pathOnEdge.node.Previous.Value, v);
if (o != -1) return pathOnEdge.node.Previous.Value;
}
throw new NotSupportedException();
}
///
/// return an opposite vertex of a given edge
///
int OppositeNode(MetroEdge edge, int v) {
if (edge.Source() == v) return edge.Target();
if (edge.Target() == v) return edge.Source();
return -1;
}
///
/// replace edges (av) and (vb) with edge (ab) on a given path
///
void UpdatePath(PathOnEdge pathOnEdge, int v, MetroEdge newEdge) {
LinkedListNode f = pathOnEdge.node;
Debug.Assert(f.Value.Source() == v || f.Value.Target() == v);
int a, b;
a = OppositeNode(f.Value, v);
if (f.Next != null && (b = OppositeNode(f.Next.Value, v)) != -1) {
Debug.Assert((a == newEdge.Source() || a == newEdge.Target()));
Debug.Assert((b == newEdge.Source() || b == newEdge.Target()));
f.Value = newEdge;
f.List.Remove(f.Next);
}
else if (f.Previous != null && (b = OppositeNode(f.Previous.Value, v)) != -1) {
Debug.Assert((a == newEdge.Source() || a == newEdge.Target()));
Debug.Assert((b == newEdge.Source() || b == newEdge.Target()));
f.Value = newEdge;
f.List.Remove(f.Previous);
}
else
throw new NotSupportedException();
}
}
}