Aetheria/Assets/Plugins/MSAGL/Miscellaneous/Phylo/PhyloTreeLayoutCalclulation.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

525 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.GraphAlgorithms;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Layout.Layered;
using Microsoft.Msagl.Core;
using Microsoft.Msagl.Core.DataStructures;
namespace Microsoft.Msagl.Prototype.Phylo {
internal class PhyloTreeLayoutCalclulation : AlgorithmBase{
Anchor[] anchors;
ProperLayeredGraph properLayeredGraph;
Database dataBase;
PhyloTree tree;
BasicGraph<Node, IntEdge> intGraph;
LayerArrays layerArrays;
SortedDictionary<int, double> gridLayerOffsets = new SortedDictionary<int, double>();
double[] layerOffsets;
double cellSize;
Dictionary<Node, double> nodeOffsets = new Dictionary<Node, double>();
Dictionary<Node, int> nodesToIndices = new Dictionary<Node, int>();
int[] originalNodeToGridLayerIndices;
Dictionary<int, int> gridLayerToLayer=new Dictionary<int,int>();
///// <summary>
///// the layout responsible for the algorithm parameters
///// </summary>
internal SugiyamaLayoutSettings LayoutSettings { get; private set; }
internal PhyloTreeLayoutCalclulation(PhyloTree phyloTreeP, SugiyamaLayoutSettings settings, BasicGraph<Node, IntEdge> intGraphP, Database dataBase) {
this.dataBase = dataBase;
this.tree = phyloTreeP;
this.LayoutSettings = settings;
this.intGraph = intGraphP;
originalNodeToGridLayerIndices = new int[intGraph.Nodes.Count];
}
protected override void RunInternal() {
if (!IsATree())
throw new InvalidDataException("the graph is not a tree");
DefineCellSize();
CalculateOriginalNodeToGridLayerIndices();
CreateLayerArraysAndProperLayeredGraph();
FillDataBase();
RunXCoordinateAssignmentsByBrandes();
CalcTheBoxFromAnchors();
StretchIfNeeded();
ProcessPositionedAnchors();
// SugiyamaLayoutSettings.ShowDataBase(this.dataBase);
RouteSplines();
}
bool IsATree() {
Set<Node> visited = new Set<Node>();
Node root = tree.Nodes.FirstOrDefault(n => !n.InEdges.Any());
if (root == null)
return false;
return IsATreeUnderNode(root, visited) && visited.Count==tree.Nodes.Count;
}
static bool IsATreeUnderNode(Node node, Set<Node> visited) {
if (visited.Contains(node)) return false;
visited.Insert(node);
return node.OutEdges.All(outEdge => IsATreeUnderNode(outEdge.Target, visited));
}
private void StretchIfNeeded() {
if (this.LayoutSettings.AspectRatio != 0) {
double aspectRatio = this.tree.Width / this.tree.Height;
StretchToDesiredAspectRatio(aspectRatio, LayoutSettings.AspectRatio);
}
}
private void StretchToDesiredAspectRatio(double aspectRatio, double desiredAR) {
if (aspectRatio > desiredAR)
StretchInYDirection(aspectRatio / desiredAR);
else if (aspectRatio < desiredAR)
StretchInXDirection(desiredAR / aspectRatio);
}
private void StretchInYDirection(double scaleFactor) {
double center = (this.tree.BoundingBox.Top + this.tree.BoundingBox.Bottom) / 2;
foreach (Anchor a in this.dataBase.Anchors) {
a.BottomAnchor *= scaleFactor;
a.TopAnchor *= scaleFactor;
a.Y = center + scaleFactor * (a.Y - center);
}
double h = this.tree.Height * scaleFactor;
this.tree.BoundingBox = new Rectangle(this.tree.BoundingBox.Left, center + h / 2, this.tree.BoundingBox.Right, center - h / 2);
}
private void StretchInXDirection(double scaleFactor) {
double center = (this.tree.BoundingBox.Left + this.tree.BoundingBox.Right) / 2;
foreach (Anchor a in this.dataBase.Anchors) {
a.LeftAnchor *= scaleFactor;
a.RightAnchor *= scaleFactor;
a.X = center + scaleFactor * (a.X - center);
}
double w = this.tree.Width * scaleFactor;
this.tree.BoundingBox =
new Rectangle(center - w / 2, tree.BoundingBox.Top,
center + w / 2, this.tree.BoundingBox.Bottom);
}
private void DefineCellSize() {
double min = double.MaxValue;
foreach (PhyloEdge e in this.tree.Edges)
min = Math.Min(min, e.Length);
this.cellSize=0.3*min;
}
private void CalculateOriginalNodeToGridLayerIndices() {
InitNodesToIndices();
FillNodeOffsets();
foreach (KeyValuePair<Node, double> kv in this.nodeOffsets) {
int nodeIndex = this.nodesToIndices[kv.Key];
int gridLayerIndex=originalNodeToGridLayerIndices[nodeIndex] = GetGridLayerIndex(kv.Value);
if (!gridLayerOffsets.ContainsKey(gridLayerIndex))
gridLayerOffsets[gridLayerIndex] = kv.Value;
}
}
private int GetGridLayerIndex(double len) {
return (int)(len / this.cellSize + 0.5);
}
private void InitNodesToIndices() {
for (int i = 0; i < this.intGraph.Nodes.Count; i++)
nodesToIndices[intGraph.Nodes[i]] = i;
}
private void FillNodeOffsets() {
FillNodeOffsets(0.0, tree.Root);
}
private void FillNodeOffsets(double p, Node node) {
nodeOffsets[node] = p;
foreach (PhyloEdge e in node.OutEdges)
FillNodeOffsets(p+e.Length, e.Target);
}
private void FillDataBase() {
foreach (IntEdge e in intGraph.Edges)
dataBase.RegisterOriginalEdgeInMultiedges(e);
SizeAnchors();
FigureYCoordinates();
}
private void FigureYCoordinates() {
double m = GetMultiplier();
int root = nodesToIndices[tree.Root];
CalculateAnchorsY(root,m,0);
for (int i = intGraph.NodeCount; i < dataBase.Anchors.Length; i++)
dataBase.Anchors[i].Y = -m * layerOffsets[layerArrays.Y[i]];
//fix layer offsets
for (int i = 0; i < layerOffsets.Length; i++)
layerOffsets[i] *= m;
}
private double GetMultiplier() {
double m = 1;
for (int i = layerArrays.Layers.Length - 1; i > 0; i--) {
double nm = GetMultiplierBetweenLayers(i);
if (nm > m)
m = nm;
}
return m;
}
private double GetMultiplierBetweenLayers(int i) {
int a = FindLowestBottomOnLayer(i);
int b = FindHighestTopOnLayer(i - 1);
double ay = NodeY(i, a);
double by = NodeY(i - 1, b);
// we need to have m*(a[y]-b[y])>=anchors[a].BottomAnchor+anchors[b].TopAnchor+layerSeparation;
double diff = ay - by;
if (diff < 0)
throw new InvalidOperationException();
double nm = (dataBase.Anchors[a].BottomAnchor + dataBase.Anchors[b].TopAnchor + LayoutSettings.LayerSeparation) / diff;
if (nm > 1)
return nm;
return 1;
}
private int FindHighestTopOnLayer(int layerIndex) {
int[] layer = layerArrays.Layers[layerIndex];
int ret = layer[0];
double top = NodeY(layerIndex, ret) + dataBase.Anchors[ret].TopAnchor;
for (int i = 1; i < layer.Length; i++) {
int node=layer[i];
double nt = NodeY(layerIndex, node) + dataBase.Anchors[node].TopAnchor;
if (nt > top) {
top = nt;
ret = node;
}
}
return ret;
}
private int FindLowestBottomOnLayer(int layerIndex) {
int[] layer = layerArrays.Layers[layerIndex];
int ret = layer[0];
double bottom = NodeY(layerIndex, ret) - dataBase.Anchors[ret].BottomAnchor;
for (int i = 1; i < layer.Length; i++) {
int node = layer[i];
double nb = NodeY(layerIndex, node) - dataBase.Anchors[node].BottomAnchor;
if (nb < bottom) {
bottom = nb;
ret = node;
}
}
return ret;
}
private double NodeY(int layer, int node) {
return - (IsOriginal(node) ? nodeOffsets[intGraph.Nodes[node]] : layerOffsets[layer]);
}
private bool IsOriginal(int node) {
return node < intGraph.NodeCount;
}
private void CalculateAnchorsY(int node, double m, double y) {
//go over original nodes
dataBase.Anchors[node].Y = -y;
foreach (IntEdge e in intGraph.OutEdges(node))
CalculateAnchorsY(e.Target, m, y + e.Edge.Length * m);
}
private void SizeAnchors() {
dataBase.Anchors = anchors = new Anchor[properLayeredGraph.NodeCount];
for (int i = 0; i < anchors.Length; i++)
anchors[i] = new Anchor(LayoutSettings.LabelCornersPreserveCoefficient);
//go over the old vertices
for (int i = 0; i < intGraph.NodeCount; i++)
CalcAnchorsForOriginalNode(i);
//go over virtual vertices
foreach (IntEdge intEdge in dataBase.AllIntEdges) {
if (intEdge.LayerEdges != null) {
foreach (LayerEdge layerEdge in intEdge.LayerEdges) {
int v = layerEdge.Target;
if (v != intEdge.Target) {
Anchor anchor = anchors[v];
if (!dataBase.MultipleMiddles.Contains(v)) {
anchor.LeftAnchor = anchor.RightAnchor = VirtualNodeWidth / 2.0f;
anchor.TopAnchor = anchor.BottomAnchor = VirtualNodeHeight / 2.0f;
} else {
anchor.LeftAnchor = anchor.RightAnchor = VirtualNodeWidth * 4;
anchor.TopAnchor = anchor.BottomAnchor = VirtualNodeHeight / 2.0f;
}
}
}
//fix label vertices
if (intEdge.Edge.Label!=null) {
int lj = intEdge.LayerEdges[intEdge.LayerEdges.Count / 2].Source;
Anchor a = anchors[lj];
double w = intEdge.LabelWidth, h = intEdge.LabelHeight;
a.RightAnchor = w;
a.LeftAnchor = LayoutSettings.NodeSeparation;
if (a.TopAnchor < h / 2.0)
a.TopAnchor = a.BottomAnchor = h / 2.0;
a.LabelToTheRightOfAnchorCenter = true;
}
}
}
}
/// <summary>
/// the width of dummy nodes
/// </summary>
static double VirtualNodeWidth {
get {
return 1;
}
}
/// <summary>
/// the height of dummy nodes
/// </summary>
double VirtualNodeHeight {
get {
return LayoutSettings.MinNodeHeight * 1.5f / 8;
}
}
void CalcAnchorsForOriginalNode(int i) {
double leftAnchor = 0;
double rightAnchor = leftAnchor;
double topAnchor = 0;
double bottomAnchor = topAnchor;
//that's what we would have without the label and multiedges
if (intGraph.Nodes != null) {
Node node = intGraph.Nodes[i];
ExtendStandardAnchors(ref leftAnchor, ref rightAnchor, ref topAnchor, ref bottomAnchor, node);
}
RightAnchorMultiSelfEdges(i, ref rightAnchor, ref topAnchor, ref bottomAnchor);
double hw = LayoutSettings.MinNodeWidth / 2;
if (leftAnchor < hw)
leftAnchor = hw;
if (rightAnchor < hw)
rightAnchor = hw;
double hh = LayoutSettings.MinNodeHeight / 2;
if (topAnchor < hh)
topAnchor = hh;
if (bottomAnchor < hh)
bottomAnchor = hh;
anchors[i] = new Anchor(leftAnchor, rightAnchor, topAnchor, bottomAnchor, this.intGraph.Nodes[i], LayoutSettings.LabelCornersPreserveCoefficient)
{Padding = this.intGraph.Nodes[i].Padding};
#if TEST_MSAGL
//anchors[i].Id = this.intGraph.Nodes[i].Id;
#endif
}
private static void ExtendStandardAnchors(ref double leftAnchor, ref double rightAnchor, ref double topAnchor, ref double bottomAnchor, Node node) {
double w = node.Width;
double h = node.Height;
w /= 2.0;
h /= 2.0;
rightAnchor = leftAnchor = w;
topAnchor = bottomAnchor = h;
}
private void RightAnchorMultiSelfEdges(int i, ref double rightAnchor, ref double topAnchor, ref double bottomAnchor) {
double delta = WidthOfSelfeEdge(i, ref rightAnchor, ref topAnchor, ref bottomAnchor);
rightAnchor += delta;
}
private double WidthOfSelfeEdge(int i, ref double rightAnchor, ref double topAnchor, ref double bottomAnchor) {
double delta = 0;
List<IntEdge> multiedges = dataBase.GetMultiedge(i, i);
//it could be a multiple self edge
if (multiedges.Count > 0) {
foreach (IntEdge e in multiedges) {
if (e.Edge.Label != null) {
rightAnchor += e.Edge.Label.Width;
if (topAnchor < e.Edge.Label.Height / 2.0)
topAnchor = bottomAnchor = e.Edge.Label.Height / 2.0f;
}
}
delta += (LayoutSettings.NodeSeparation + LayoutSettings.MinNodeWidth) * multiedges.Count;
}
return delta;
}
private void RouteSplines() {
Layout.Layered.Routing routing = new Layout.Layered.Routing(LayoutSettings, this.tree, dataBase, this.layerArrays, this.properLayeredGraph, null);
routing.Run();
}
private void RunXCoordinateAssignmentsByBrandes() {
XCoordsWithAlignment.CalculateXCoordinates(this.layerArrays, this.properLayeredGraph, this.tree.Nodes.Count, this.dataBase.Anchors, this.LayoutSettings.NodeSeparation);
}
private void CreateLayerArraysAndProperLayeredGraph() {
int numberOfLayers = this.gridLayerOffsets.Count;
this.layerOffsets=new double[numberOfLayers];
int i = numberOfLayers-1;
foreach (KeyValuePair<int, double> kv in this.gridLayerOffsets) {
layerOffsets[i] = kv.Value;
gridLayerToLayer[kv.Key] = i--;
}
int nOfNodes=CountTotalNodesIncludingVirtual(nodesToIndices[tree.Root]);
////debugging !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//int tt = 0;
//foreach (IntEdge ie in this.intGraph.Edges)
// tt += this.OriginalNodeLayer(ie.Source) - this.OriginalNodeLayer(ie.Target) - 1;
//if (tt + this.intGraph.Nodes.Count != nOfNodes)
// throw new Exception();
int[] layering = new int[nOfNodes];
List<int>[] layers = new List<int>[numberOfLayers];
for (i = 0; i < numberOfLayers; i++)
layers[i] = new List<int>();
WalkTreeAndInsertLayerEdges(layering, layers);
this.layerArrays = new LayerArrays(layering);
int[][]ll=layerArrays.Layers=new int[numberOfLayers][];
i = 0;
foreach (List<int> layer in layers) {
ll[i++] = layer.ToArray();
}
this.properLayeredGraph = new ProperLayeredGraph(intGraph);
}
private int CountTotalNodesIncludingVirtual(int node) {
int ret = 1;
foreach (IntEdge edge in this.intGraph.OutEdges(node))
ret += NumberOfVirtualNodesOnEdge(edge) + CountTotalNodesIncludingVirtual(edge.Target);
return ret;
}
private int NumberOfVirtualNodesOnEdge(IntEdge edge) {
return OriginalNodeLayer(edge.Source) - OriginalNodeLayer(edge.Target) - 1;
}
private int OriginalNodeLayer(int node) {
return gridLayerToLayer[originalNodeToGridLayerIndices[node]];
}
private void WalkTreeAndInsertLayerEdges(int[] layering, List<int>[] layers) {
int virtualNode = this.intGraph.NodeCount;
int root = nodesToIndices[tree.Root];
int l;
layering[root] = l = OriginalNodeLayer(root);
layers[l].Add(root);
WalkTreeAndInsertLayerEdges(layering, layers, root , ref virtualNode);
}
private void WalkTreeAndInsertLayerEdges(int[] layering, List<int>[] layers, int node, ref int virtualNode) {
foreach (IntEdge edge in this.intGraph.OutEdges(node))
InsertLayerEdgesForEdge(edge, layering, ref virtualNode, layers);
}
private void InsertLayerEdgesForEdge(IntEdge edge, int[] layering, ref int virtualNode, List<int>[] layers) {
int span = OriginalNodeLayer(edge.Source) - OriginalNodeLayer(edge.Target);
edge.LayerEdges=new LayerEdge[span];
for (int i = 0; i < span; i++)
edge.LayerEdges[i] = new LayerEdge(GetSource(i, edge, ref virtualNode), GetTarget(i, span, edge, virtualNode), edge.CrossingWeight);
int l = OriginalNodeLayer(edge.Source) - 1;
for (int i = 0; i < span; i++) {
int node=edge.LayerEdges[i].Target;
layering[node] = l;
layers[l--].Add(node);
}
WalkTreeAndInsertLayerEdges(layering, layers, edge.Target, ref virtualNode);
}
static private int GetTarget(int i, int span, IntEdge edge, int virtualNode) {
if (i < span-1)
return virtualNode;
return edge.Target;
}
static private int GetSource(int i, IntEdge edge, ref int virtualNode) {
if (i > 0)
return virtualNode++;
return edge.Source;
}
void ProcessPositionedAnchors() {
for (int i = 0; i < this.tree.Nodes.Count; i++)
intGraph.Nodes[i].Center=anchors[i].Origin;
}
private void CalcTheBoxFromAnchors() {
if (anchors.Length > 0) {
Rectangle box = new Rectangle(anchors[0].Left, anchors[0].Top, anchors[0].Right, anchors[0].Bottom);
for (int i = 1; i < anchors.Length; i++) {
Anchor a = anchors[i];
box.Add(a.LeftTop);
box.Add(a.RightBottom);
}
double m = Math.Max(box.Width, box.Height);
double delta = this.tree.Margins / 100.0 * m;
Point del = new Point(-delta, delta);
box.Add(box.LeftTop + del);
box.Add(box.RightBottom - del);
this.tree.BoundingBox = box;
}
}
}
}