using System;
using System.Collections.Generic;
using System.Diagnostics;
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 {
///
/// greedy bundle map ordering based on path comparison
///
public class GeneralMetroMapOrdering : IMetroMapOrderingAlgorithm {
///
/// bundle lines
///
readonly List Metrolines;
Dictionary bundles;
///
/// Initialize bundle graph and build the ordering
///
internal GeneralMetroMapOrdering(List Metrolines) {
this.Metrolines = Metrolines;
BuildOrder();
}
///
/// Get the ordering of lines on station u with respect to the edge (u->v)
///
#if SHARPKIT //http://code.google.com/p/sharpkit/issues/detail?id=203
//SharpKit/Colin - Interface implementations
public IEnumerable GetOrder(Station u, Station v) {
#else
IEnumerable IMetroMapOrderingAlgorithm.GetOrder(Station u, Station v) {
#endif
var pointPair = new PointPair(u.Position, v.Position);
var orderedMetrolineListForUv = bundles[pointPair].Metrolines;
if (u.Position == pointPair.First) {
foreach (var Metroline in orderedMetrolineListForUv) {
yield return Metroline;
}
}
else {
for (int i = orderedMetrolineListForUv.Count - 1; i >= 0; i--)
yield return orderedMetrolineListForUv[i];
}
}
///
/// Get the index of line on the edge (u->v) and node u
///
#if SHARPKIT //http://code.google.com/p/sharpkit/issues/detail?id=203
//SharpKit/Colin - Interface implementations
public int GetLineIndexInOrder(Station u, Station v, Metroline Metroline) {
#else
int IMetroMapOrderingAlgorithm.GetLineIndexInOrder(Station u, Station v, Metroline Metroline) {
#endif
var edge = new PointPair(u.Position, v.Position);
var reversed = u.Position != edge.First;
var d = bundles[edge].LineIndexInOrder;
return !reversed ? d[Metroline] : d.Count - 1 - d[Metroline];
}
///
/// Do the main job
///
void BuildOrder() {
bundles = new Dictionary();
//initialization
foreach (var Metroline in Metrolines) {
for (var p = Metroline.Polyline.StartPoint; p.Next != null; p = p.Next) {
var e = new PointPair(p.Point, p.Next.Point);
PointPairOrder li;
if (!bundles.TryGetValue(e, out li))
bundles[e] = li = new PointPairOrder();
li.Add(Metroline);
}
}
foreach (var edge in bundles)
BuildOrder(edge.Key, edge.Value);
}
///
/// Build order for edge (u->v)
///
void BuildOrder(PointPair pair, PointPairOrder order) {
if (order.orderFixed) return;
order.Metrolines.Sort((line0, line1) => CompareLines(line0, line1, pair.First, pair.Second));
//save order
order.orderFixed = true;
order.LineIndexInOrder = new Dictionary();
for (int i = 0; i < order.Metrolines.Count; i++)
order.LineIndexInOrder[order.Metrolines[i]] = i;
}
///
/// Compare two lines on station u with respect to edge (u->v)
///
int CompareLines(Metroline ml0, Metroline ml1, Point u, Point v) {
PolylinePoint polylinePoint0;
Func next0;
Func prev0;
FindStationOnLine(u, v, ml0, out polylinePoint0, out next0, out prev0);
PolylinePoint polylinePoint1;
Func next1;
Func prev1;
FindStationOnLine(u, v, ml1, out polylinePoint1, out next1, out prev1);
//go backward
var p0 = polylinePoint0;
var p1 = polylinePoint1;
PolylinePoint p00, p11 = null;
while ((p00 = prev0(p0)) != null && (p11 = prev1(p1)) != null && p00.Point == p11.Point) {
var edge = new PointPair(p00.Point, p0.Point);
if (bundles[edge].orderFixed) {
return CompareOnFixedOrder(edge, ml0, ml1, p00.Point !=edge.First);
}
p0 = p00;
p1 = p11;
}
if (p00 != null && p11 != null) { //we have a backward fork
var forkBase = p0.Point;
return IsLeft(next0(p0).Point - forkBase,
p00.Point - forkBase,
p11.Point - forkBase);
}
//go forward
p0 = polylinePoint0;
p1 = polylinePoint1;
while ((p00 = next0(p0)) != null && (p11 = next1(p1)) != null && p00.Point == p11.Point) {
var edge = new PointPair(p00.Point, p0.Point);
if (bundles[edge].orderFixed)
return CompareOnFixedOrder(edge, ml0, ml1, p0.Point!=edge.First);
p0 = p00;
p1 = p11;
}
if (p00 != null && p11 != null) {//compare forward fork
var forkBase = p0.Point;
return -IsLeft(prev0(p0).Point - forkBase,
p00.Point - forkBase,
p11.Point - forkBase);
}
//these are multiple edges
return ml0.Index.CompareTo(ml1.Index);
}
int CompareOnFixedOrder(PointPair edge, Metroline ml0, Metroline ml1, bool reverse) {
var mlToIndex = bundles[edge].LineIndexInOrder;
int r = reverse ? -1 : 1;
return r * mlToIndex[ml0].CompareTo(mlToIndex[ml1]);
}
///
/// Reimplement it in more efficient way!!! (cache indexes)
///
void FindStationOnLine(Point u, Point v, Metroline Metroline, out PolylinePoint polyPoint, out Func next,
out Func prev) {
for (var p = Metroline.Polyline.StartPoint; p.Next != null; p = p.Next) {
if (p.Point == u && p.Next.Point == v) {
next = Next;
prev = Prev;
polyPoint = p;
return;
}
if (p.Point == v && p.Next.Point == u) {
prev = Next;
next = Prev;
polyPoint = p.Next;
return;
}
}
throw new InvalidOperationException();
}
static PolylinePoint Next(PolylinePoint p) {
return p.Next;
}
static PolylinePoint Prev(PolylinePoint p) {
return p.Prev;
}
///
/// Compare polar angles of v1 and v2 with respect to v0
/// (v1 lyes to the left of v2 ?)
///
///
/// -1 if v1 lyes to the left of v2
/// 1 if v1 lyes to the right of v2
/// 0 if v1 and v2 are collinear (and codirectinal)
///
static int IsLeft(Point v0, Point v1, Point v2) {
return Point.GetOrientationOf3Vectors(v0, v1, v2);
}
}
}