Aetheria/Assets/Plugins/MSAGL/Layout/Layered/Ordering/Ordering.cs
Reinier Vens dbc522a224 Weapos Rejiggered
Modified weapon scripts to eliminate specialized projectile behavior - allows charged weapons to have projectiles and modify weapon stats when charged
Added machine gun weapon & VFX
Moved third party code to Plugins folder to improve compile times
2021-01-31 03:52:32 +00:00

582 lines
No EOL
21 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Msagl.Core;
namespace Microsoft.Msagl.Layout.Layered {
/// <summary>
/// Following "A technique for Drawing Directed Graphs" of Gansner, Koutsofios, North and Vo
/// Works on the layered graph. Also see GraphLayout.pdfhttps://www.researchgate.net/profile/Lev_Nachmanson/publication/30509007_Drawing_graphs_with_GLEE/links/54b6b2930cf2e68eb27edf71/Drawing-graphs-with-GLEE.pdf
/// </summary>
internal partial class Ordering : AlgorithmBase{
#region Fields
bool balanceVirtAndOrigNodes;
bool hasCrossWeights;
LayerArrays layerArrays;
int[][] layerArraysCopy;
int[] layering;
int[][] layers;
OrderingMeasure measure;
int nOfLayers;
double[] optimalOriginalGroupSize;
double[] optimalVirtualGroupSize;
ProperLayeredGraph properLayeredGraph;
/// <summary>
/// this field is needed to randomly choose between transposing
/// and not transposing in the adjacent exchange
/// </summary>
Random random;
SugiyamaLayoutSettings settings;
int startOfVirtNodes;
bool tryReverse = true;
int NoGainStepsBound {
get {
return SugiyamaLayoutSettings.NoGainAdjacentSwapStepsBound*
SugiyamaLayoutSettings.RepetitionCoefficientForOrdering;
}
}
SugiyamaLayoutSettings SugiyamaLayoutSettings {
get { return settings; }
}
/// <summary>
/// gets the random seed for some random choices inside of layer ordering
/// </summary>
int SeedOfRandom {
get { return SugiyamaLayoutSettings.RandomSeedForOrdering; }
}
#endregion
int[] X;
Ordering(ProperLayeredGraph graphPar, bool tryReverse, LayerArrays layerArraysParam, int startOfVirtualNodes, bool balanceVirtualAndOrigNodes, bool hasCrossWeights, SugiyamaLayoutSettings settings) {
this.tryReverse = tryReverse;
startOfVirtNodes = startOfVirtualNodes;
layerArrays = layerArraysParam;
layering = layerArraysParam.Y;
nOfLayers = layerArraysParam.Layers.Length;
layers = layerArraysParam.Layers;
balanceVirtAndOrigNodes = balanceVirtualAndOrigNodes;
properLayeredGraph = graphPar;
this.hasCrossWeights = hasCrossWeights;
this.settings = settings;
random = new Random(SeedOfRandom);
}
/// <summary>
/// an upper limit on a number of passes in layer ordering
/// </summary>
int MaxOfIterations {
get {
return SugiyamaLayoutSettings.MaxNumberOfPassesInOrdering*
SugiyamaLayoutSettings.RepetitionCoefficientForOrdering;
}
}
internal static void OrderLayers(ProperLayeredGraph graph,
LayerArrays layerArrays,
int startOfVirtualNodes,
bool balanceVirtualAndOriginalNodes,
SugiyamaLayoutSettings settings, CancelToken cancelToken) {
bool hasCrossWeight = false;
foreach (LayerEdge le in graph.Edges)
if (le.CrossingWeight != 1) {
hasCrossWeight = true;
break;
}
var o = new Ordering(graph, true, layerArrays, startOfVirtualNodes, balanceVirtualAndOriginalNodes, hasCrossWeight, settings);
o.Run(cancelToken);
}
protected override void RunInternal()
{
#if DEBUGORDERING
if (graph.NumberOfVertices != layering.Length)
throw new System.Exception("the layering does not correspond to the graph");
foreach (IntEdge e in graph.Edges)
if (layering[e.Source] - layering[e.Target] != 1)
throw new System.Exception("the edge in the graph does not span exactly one layer:" + e);
#endif
#if PPC // Parallel -- susanlwo
LayerArrays secondLayers = null;
Ordering revOrdering = null;
System.Threading.Tasks.Task t = null;
if (/*orderingMeasure.x>0 &&*/ tryReverse) {
secondLayers = layerArrays.ReversedClone();
revOrdering = new Ordering(properLayeredGraph.ReversedClone(), false, secondLayers, startOfVirtNodes, balanceVirtAndOrigNodes, this.hasCrossWeights, settings);
// note: below we need to pass the CancelToken from this thread into the new thread, to make sure a previous thread from the ThreadPool's
// thread static token (that may be in a cancelled state) is picked up.
t = System.Threading.Tasks.Task.Factory.StartNew(() => revOrdering.Run(this.CancelToken));
}
Calculate();
if (/*orderingMeasure.x>0 &&*/ tryReverse) {
t.Wait();
if (revOrdering.measure < measure) {
for (int j = 0; j < nOfLayers; j++)
secondLayers.Layers[j].CopyTo(layerArrays.Layers[nOfLayers - 1 - j], 0);
layerArrays.UpdateXFromLayers();
}
}
#else
Calculate();
if ( /*orderingMeasure.x>0 &&*/ tryReverse) {
LayerArrays secondLayers = layerArrays.ReversedClone();
var revOrdering = new Ordering(properLayeredGraph.ReversedClone(), false, secondLayers, startOfVirtNodes, balanceVirtAndOrigNodes,
hasCrossWeights, settings);
revOrdering.Run();
if (revOrdering.measure < measure) {
for (int j = 0; j < nOfLayers; j++)
secondLayers.Layers[j].CopyTo(layerArrays.Layers[nOfLayers - 1 - j], 0);
layerArrays.UpdateXFromLayers();
}
}
#endif
}
void Calculate() {
Init();
CloneLayers(layers, ref layerArraysCopy);
int countOfNoGainSteps = 0;
measure = new OrderingMeasure(layerArraysCopy, GetCrossingsTotal(properLayeredGraph, layerArrays),
startOfVirtNodes, optimalOriginalGroupSize,
optimalVirtualGroupSize);
//Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < MaxOfIterations && countOfNoGainSteps < NoGainStepsBound && !measure.IsPerfect(); i++) {
this.ProgressStep();
bool up = i%2 == 0;
LayerByLayerSweep(up);
if (!balanceVirtAndOrigNodes)
AdjacentExchange();
else
AdjacentExchangeWithBalancingVirtOrigNodes();
var newMeasure = new OrderingMeasure(layerArrays.Layers,
GetCrossingsTotal(properLayeredGraph, layerArrays),
startOfVirtNodes,
optimalOriginalGroupSize, optimalVirtualGroupSize);
if (measure < newMeasure) {
Restore();
countOfNoGainSteps++;
} else if (newMeasure < measure || HeadOfTheCoin()) {
countOfNoGainSteps = 0;
CloneLayers(layers, ref layerArraysCopy);
measure = newMeasure;
}
}
//Console.WriteLine("DONE == Calcuate {0}\n\n", sw.ElapsedMilliseconds);
}
internal static void CloneLayers(int[][] layers, ref int[][] layerArraysCopy) {
if (layerArraysCopy == null) {
layerArraysCopy = (int[][]) layers.Clone();
for (int i = 0; i < layers.Length; i++)
layerArraysCopy[i] = (int[]) layers[i].Clone();
} else
for (int i = 0; i < layers.Length; i++)
layers[i].CopyTo(layerArraysCopy[i], 0);
}
void Restore() {
layerArrays.UpdateLayers(layerArraysCopy);
}
void LayerByLayerSweep(bool up) {
if (up) {
for (int i = 1; i < nOfLayers; i++)
SweepLayer(i, true);
} else
for (int i = nOfLayers - 2; i >= 0; i--)
SweepLayer(i, false);
}
//static int count;
/// <summary>
/// the layer layer-1 is fixed if
/// upperLayer us true and layer+1 is fixed in
/// the opposite case
/// the layer with index "layer" is updated
/// </summary>
/// <param name="layer"></param>
/// <param name="upperLayer">upperLayer means if "layer" is the upperLayer
/// of the strip</param>
void SweepLayer(int layer, bool upperLayer) {
this.ProgressStep();
int[] l = layers[layer];
var medianValues = new float[l.Length];
for (int i = 0; i < medianValues.Length; i++)
medianValues[i] = WMedian(l[i], upperLayer);
Sort(layer, medianValues);
//update X
int[] vertices = layerArrays.Layers[layer];
for (int i = 0; i < vertices.Length; i++)
layerArrays.X[vertices[i]] = i;
}
/// <summary>
/// sorts layerToSort according to medianValues
/// if medianValues[i] is -1 then layer[i] does not move
/// </summary>
/// <param name="layerToSort"></param>
/// <param name="medianValues"></param>
void Sort(int layerToSort, float[] medianValues) {
var s = new SortedDictionary<float, object>();
int[] vertices = layers[layerToSort];
int i = 0;
foreach (float m in medianValues) {
int v = vertices[i++];
if (m == -1.0)
continue;
if (!s.ContainsKey(m))
s[m] = v;
else {
object o = s[m];
var al = o as List<int>;
if (al != null) {
if (HeadOfTheCoin())
al.Add(v);
else {
//stick it in the middle
int j = random.Next(al.Count);
int k = al[j];
al[j] = v;
al.Add(k);
}
} else {
var io = (int) o;
al = new List<int>();
s[m] = al;
if (HeadOfTheCoin()) {
al.Add(io);
al.Add(v);
} else {
al.Add(v);
al.Add(io);
}
}
}
}
var senum = s.GetEnumerator();
for (i = 0; i < vertices.Length;) {
if (medianValues[i] != -1) {
senum.MoveNext();
object o = senum.Current.Value;
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=347
//SharpKit/Colin - is check gets converted to true
if (o.GetType() == typeof(int))
#else
if (o is int)
#endif
vertices[i++] = (int) o;
else {
var al = o as List<int>;
foreach (int v in al) {
//find the first empty spot
while (medianValues[i] == -1)
i++;
vertices[i++] = v;
}
}
} else
i++;
}
}
float WMedian(int node, bool theMedianGoingDown) {
IEnumerable<LayerEdge> edges;
int p;
if (theMedianGoingDown) {
edges = properLayeredGraph.OutEdges(node);
p = properLayeredGraph.OutEdgesCount(node);
} else {
edges = properLayeredGraph.InEdges(node);
p = properLayeredGraph.InEdgesCount(node);
}
if (p == 0)
return -1.0f;
var parray = new int[p]; //we have no multiple edges
int i = 0;
if (theMedianGoingDown)
foreach (LayerEdge e in edges)
parray[i++] = X[e.Target];
else
foreach (LayerEdge e in edges)
parray[i++] = X[e.Source];
Array.Sort(parray);
int m = p/2;
if (p%2 == 1)
return parray[m];
if (p == 2)
return 0.5f*((float) parray[0] + (float) parray[1]);
float left = parray[m - 1] - parray[0];
float right = parray[p - 1] - parray[m];
return ((float) parray[m - 1]*left + (float) parray[m]*right)/(left + right);
}
/// <summary>
/// Just depth search and assign the index saying when the node was visited
/// </summary>
void Init() {
var counts = new int[nOfLayers];
//the initial layers are set by following the order of the
//depth first traversal inside one layer
var q = new Stack<int>();
//enqueue all sources of the graph
for (int i = 0; i < properLayeredGraph.NodeCount; i++)
if (properLayeredGraph.InEdgesCount(i) == 0)
q.Push(i);
var visited = new bool[properLayeredGraph.NodeCount];
while (q.Count > 0) {
int u = q.Pop();
int l = layerArrays.Y[u];
layerArrays.Layers[l][counts[l]] = u;
layerArrays.X[u] = counts[l];
counts[l]++;
foreach (int v in properLayeredGraph.Succ(u))
if (!visited[v]) {
visited[v] = true;
q.Push(v);
}
}
X = layerArrays.X;
if (balanceVirtAndOrigNodes)
InitOptimalGroupSizes();
}
void InitOptimalGroupSizes() {
optimalOriginalGroupSize = new double[nOfLayers];
optimalVirtualGroupSize = new double[nOfLayers];
for (int i = 0; i < nOfLayers; i++)
InitOptimalGroupSizesForLayer(i);
}
void InitOptimalGroupSizesForLayer(int i) {
//count original and virtual nodes
int originals = 0;
foreach (int j in layers[i])
if (j < startOfVirtNodes)
originals++;
int virtuals = layers[i].Length - originals;
if (originals < virtuals) {
optimalOriginalGroupSize[i] = 1;
optimalVirtualGroupSize[i] = (double) virtuals/(originals + 1);
} else {
optimalVirtualGroupSize[i] = 1;
optimalOriginalGroupSize[i] = (double) originals/(virtuals + 1);
}
}
internal static int GetCrossingsTotal(ProperLayeredGraph properLayeredGraph, LayerArrays layerArrays) {
int x = 0;
for (int i = 0; i < layerArrays.Layers.Length - 1; i++)
x += GetCrossingCountFromStrip(i, properLayeredGraph, layerArrays);
return x;
}
///// <summary>
///// This method can be improved: see the paper Simple And Efficient ...
///// </summary>
///// <param name="graph"></param>
///// <param name="layerArrays"></param>
///// <param name="bottom">bottom of the strip</param>
///// <returns></returns>
static int GetCrossingCountFromStrip(int bottom, ProperLayeredGraph properLayeredGraph, LayerArrays layerArrays) {
int[] topVerts = layerArrays.Layers[bottom + 1];
int[] bottomVerts = layerArrays.Layers[bottom];
if (bottomVerts.Length <= topVerts.Length)
return GetCrossingCountFromStripWhenBottomLayerIsShorter(bottomVerts, properLayeredGraph, layerArrays);
else
return GetCrossingCountFromStripWhenTopLayerIsShorter(topVerts, bottomVerts, properLayeredGraph,
layerArrays);
}
static int GetCrossingCountFromStripWhenTopLayerIsShorter(int[] topVerts, int[] bottomVerts,
ProperLayeredGraph properLayeredGraph,
LayerArrays layerArrays) {
LayerEdge[] edges = EdgesOfStrip(bottomVerts, properLayeredGraph);
Array.Sort(edges, new EdgeComparerByTarget(layerArrays.X));
//find first n such that 2^n >=topVerts.Length
int n = 1;
while (n < topVerts.Length)
n *= 2;
//init the accumulator tree
var tree = new int[2*n - 1];
n--; // the first bottom node starts from n now
int cc = 0; //number of crossings
foreach (LayerEdge edge in edges) {
int index = n + layerArrays.X[edge.Source];
int ew = edge.CrossingWeight;
tree[index] += ew;
while (index > 0) {
if (index%2 != 0)
cc += ew*tree[index + 1]; //intersect everything accumulated in the right sibling
index = (index - 1)/2;
tree[index] += ew;
}
}
return cc;
}
static int GetCrossingCountFromStripWhenBottomLayerIsShorter(int[] bottomVerts,
ProperLayeredGraph properLayeredGraph,
LayerArrays layerArrays) {
LayerEdge[] edges = EdgesOfStrip(bottomVerts, properLayeredGraph);
Array.Sort(edges, new EdgeComparerBySource(layerArrays.X));
//find first n such that 2^n >=bottomVerts.Length
int n = 1;
while (n < bottomVerts.Length)
n *= 2;
//init accumulator
var tree = new int[2*n - 1];
n--; // the first bottom node starts from n now
int cc = 0; //number of crossings
foreach (LayerEdge edge in edges) {
int index = n + layerArrays.X[edge.Target];
int ew = edge.CrossingWeight;
tree[index] += ew;
while (index > 0) {
if (index%2 != 0)
cc += ew*tree[index + 1]; //intersect everything accumulated in the right sibling
index = (index - 1)/2;
tree[index] += ew;
}
}
return cc;
}
static LayerEdge[] EdgesOfStrip(int[] bottomVerts, ProperLayeredGraph properLayeredGraph) {
LayerEdge[] edges = (from v in bottomVerts
from e in properLayeredGraph.InEdges(v)
select e).ToArray();
return edges;
}
/*
void Visualize()
{
int x = 0;
for (int i = 0; i < layers.Length - 1; i++)
x += GetCrossingCountFromStrip(i);
GraphViewer.Graph g = new GraphViewer.Graph(x.ToString(),"");
//same layers
foreach (int[] l in layers)
{
List<string> r = new List<string>();
foreach (int i in l)
{
r.Add(i.ToString());
GraphViewer.Node n=g.IncludeNode(i.ToString());
}
g.SetSameLayer(r);
}
foreach (int[] l in layers)
{
for (int i = 0; i < l.Length - 1; i++)
{
GraphViewer.Edge e=g.AddEdge(l[i].ToString(), l[i + 1].ToString());
e.attr.Color = System.Drawing.Color.White;
}
}
foreach (LayerEdge ie in this.graph.Edges)
g.AddEdge(ie.Source.ToString(), ie.Target.ToString());
Form f = new Form();
GraphViewer.DOTViewer v = new DOTViewer();
v.Graph = g;
f.SuspendLayout();
f.Controls.Add(v);
f.Size =
new System.Drawing.Size(System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Size.Width / 2, System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Size.Height / 2);
v.Dock = DockStyle.Fill;
f.ResumeLayout();
f.ShowDialog();
}
*/
}
}