Aetheria/Assets/Plugins/MSAGL/Layout/Layered/Ordering/ConstrainedOrdering.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

614 lines
No EOL
25 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Msagl.Core.DataStructures;
using Microsoft.Msagl.Core.GraphAlgorithms;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.ProjectionSolver;
namespace Microsoft.Msagl.Layout.Layered {
internal class ConstrainedOrdering {
readonly GeometryGraph geometryGraph;
readonly BasicGraph<Node, IntEdge> intGraph;
internal ProperLayeredGraph ProperLayeredGraph;
readonly int[] initialLayering;
LayerInfo[] layerInfos;
internal LayerArrays LayerArrays;
readonly HorizontalConstraintsForSugiyama horizontalConstraints;
int numberOfNodesOfProperGraph;
readonly Database database;
double[][] xPositions;
int[][] yetBestLayers;
readonly List<IntEdge> verticalEdges = new List<IntEdge>();
readonly AdjacentSwapsWithConstraints adjSwapper;
SugiyamaLayoutSettings settings;
int numberOfLayers = -1;
int noGainSteps;
const int MaxNumberOfNoGainSteps=5;
int NumberOfLayers {
get {
if (numberOfLayers > 0)
return numberOfLayers;
return numberOfLayers = initialLayering.Max(i => i + 1);
}
}
double NodeSeparation() {
return settings.NodeSeparation;
}
internal ConstrainedOrdering(
GeometryGraph geomGraph,
BasicGraph<Node, IntEdge> basicIntGraph,
int[] layering,
Dictionary<Node, int> nodeIdToIndex,
Database database,
SugiyamaLayoutSettings settings) {
this.settings = settings;
horizontalConstraints = settings.HorizontalConstraints;
horizontalConstraints.PrepareForOrdering(nodeIdToIndex, layering);
geometryGraph = geomGraph;
this.database = database;
intGraph = basicIntGraph;
initialLayering = layering;
//this has to be changed only to insert layers that are needed
if (NeedToInsertLayers(layering)) {
for (int i = 0; i < layering.Length; i++)
layering[i] *= 2;
LayersAreDoubled = true;
numberOfLayers = -1;
}
PrepareProperLayeredGraphAndFillLayerInfos();
adjSwapper = new AdjacentSwapsWithConstraints(
LayerArrays,
HasCrossWeights(),
ProperLayeredGraph,
layerInfos);
}
bool LayersAreDoubled { get; set; }
bool NeedToInsertLayers(int[] layering) {
return ExistsShortLabeledEdge(layering, intGraph.Edges) ||
ExistsShortMultiEdge(layering, database.Multiedges);
}
static bool ExistsShortMultiEdge(int[] layering, Dictionary<IntPair, List<IntEdge>> multiedges) {
return multiedges.Any(multiedge => multiedge.Value.Count > 2 && layering[multiedge.Key.x] == 1 + layering[multiedge.Key.y]);
}
internal void Calculate() {
AllocateXPositions();
var originalGraph = intGraph.Nodes[0].GeometryParent as GeometryGraph;
LayeredLayoutEngine.CalculateAnchorSizes(database, out database.anchors, ProperLayeredGraph, originalGraph, intGraph, settings);
LayeredLayoutEngine.CalcInitialYAnchorLocations(LayerArrays, 500, geometryGraph, database, intGraph, settings, LayersAreDoubled);
Order();
}
ConstrainedOrderMeasure CreateMeasure() {
return new ConstrainedOrderMeasure(Ordering.GetCrossingsTotal(ProperLayeredGraph, LayerArrays));
}
bool HasCrossWeights() {
return ProperLayeredGraph.Edges.Any(le => le.CrossingWeight != 1);
}
static bool ExistsShortLabeledEdge(int[] layering, IEnumerable<IntEdge> edges) {
return edges.Any(edge => layering[edge.Source] == layering[edge.Target] + 1 && edge.Edge.Label != null);
}
void AllocateXPositions() {
xPositions = new double[NumberOfLayers][];
for (int i = 0; i < NumberOfLayers; i++)
xPositions[i] = new double[LayerArrays.Layers[i].Length];
}
void Order() {
CreateInitialOrderInLayers();
TryPushingOutStrangersFromHorizontalBlocks();
int n = 5;
ConstrainedOrderMeasure measure = null;
while (n-- > 0 && noGainSteps <= MaxNumberOfNoGainSteps) {
SetXPositions();
ConstrainedOrderMeasure newMeasure = CreateMeasure();
if (measure == null || newMeasure < measure) {
noGainSteps = 0;
Ordering.CloneLayers(LayerArrays.Layers, ref yetBestLayers);
measure = newMeasure;
} else {
noGainSteps++;
RestoreState();
}
}
#region old code
/*
int noGainSteps = 0;
for (int i = 0; i < NumberOfSweeps && noGainSteps <= MaxNumberOfNoGainSteps && !measure.Perfect(); i++) {
SweepDown(false);
SweepUp(false);
ConstrainedOrderMeasure newMeasure = CreateMeasure();
if (newMeasure < measure) {
noGainSteps = 0;
Ordering.CloneLayers(LayerArrays.Layers, ref yetBestLayers);
measure = newMeasure;
} else {
noGainSteps++;
RestoreState();
}
}
SwitchXPositions();
SweepUpWithoutChangingLayerOrder(true);
SwitchXPositions();
SweepDownWithoutChangingLayerOrder(true);
AverageXPositions();
*/
#endregion
}
void SetXPositions() {
ISolverShell solver = InitSolverWithoutOrder();
ImproveWithAdjacentSwaps();
PutLayerNodeSeparationsIntoSolver(solver);
solver.Solve();
SortLayers(solver);
for (int i = 0; i < LayerArrays.Y.Length; i++)
database.Anchors[i].X = solver.GetVariableResolvedPosition(i);
}
ISolverShell InitSolverWithoutOrder() {
ISolverShell solver=CreateSolver();
InitSolverVars(solver);
PutLeftRightConstraintsIntoSolver(solver);
PutVerticalConstraintsIntoSolver(solver);
AddGoalsToKeepProperEdgesShort(solver);
AddGoalsToKeepFlatEdgesShort(solver);
return solver;
}
void SortLayers(ISolverShell solver) {
for (int i = 0; i < LayerArrays.Layers.Length; i++)
SortLayerBasedOnSolution(LayerArrays.Layers[i], solver);
}
void AddGoalsToKeepFlatEdgesShort(ISolverShell solver) {
foreach (var layerInfo in layerInfos)
AddGoalToKeepFlatEdgesShortOnBlockLevel(layerInfo, solver);
}
void InitSolverVars(ISolverShell solver) {
for (int i = 0; i < LayerArrays.Y.Length; i++)
solver.AddVariableWithIdealPosition(i, 0);
}
void AddGoalsToKeepProperEdgesShort(ISolverShell solver) {
foreach (var edge in ProperLayeredGraph.Edges)
solver.AddGoalTwoVariablesAreClose(edge.Source, edge.Target, PositionOverBaricenterWeight);
}
void PutVerticalConstraintsIntoSolver(ISolverShell solver) {
foreach (var pair in horizontalConstraints.VerticalInts) {
solver.AddGoalTwoVariablesAreClose(pair.Item1, pair.Item2, ConstrainedVarWeight);
}
}
void PutLeftRightConstraintsIntoSolver(ISolverShell solver) {
foreach (var pair in horizontalConstraints.LeftRighInts) {
solver.AddLeftRightSeparationConstraint(pair.Item1, pair.Item2, SimpleGapBetweenTwoNodes(pair.Item1, pair.Item2));
}
}
void PutLayerNodeSeparationsIntoSolver(ISolverShell solver) {
foreach (var layer in LayerArrays.Layers) {
for (int i = 0; i < layer.Length - 1; i++) {
int l = layer[i];
int r = layer[i + 1];
solver.AddLeftRightSeparationConstraint(l, r, SimpleGapBetweenTwoNodes(l, r));
}
}
}
void ImproveWithAdjacentSwaps() {
adjSwapper.DoSwaps();
}
void TryPushingOutStrangersFromHorizontalBlocks() {
}
void CreateInitialOrderInLayers() {
//the idea is to topologically ordering all nodes horizontally, by using vertical components, then fill the layers according to this order
Dictionary<int, int> nodesToVerticalComponentsRoots = CreateVerticalComponents();
IEnumerable<IntPair> liftedLeftRightRelations = LiftLeftRightRelationsToComponentRoots(nodesToVerticalComponentsRoots).ToArray();
int[] orderOfVerticalComponentRoots = TopologicalSort.GetOrderOnEdges(liftedLeftRightRelations);
FillLayersWithVerticalComponentsOrder(orderOfVerticalComponentRoots, nodesToVerticalComponentsRoots);
LayerArrays.UpdateXFromLayers();
}
void FillLayersWithVerticalComponentsOrder(int[] order, Dictionary<int, int> nodesToVerticalComponentsRoots) {
Dictionary<int, List<int>> componentRootsToComponents = CreateComponentRootsToComponentsMap(nodesToVerticalComponentsRoots);
var alreadyInLayers = new bool[LayerArrays.Y.Length];
var runninglayerCounts = new int[LayerArrays.Layers.Length];
foreach (var vertCompRoot in order)
PutVerticalComponentIntoLayers(EnumerateVertComponent(componentRootsToComponents, vertCompRoot), runninglayerCounts, alreadyInLayers);
for (int i = 0; i < ProperLayeredGraph.NodeCount; i++)
if (alreadyInLayers[i] == false)
AddVertToLayers(i, runninglayerCounts, alreadyInLayers);
}
IEnumerable<int> EnumerateVertComponent(Dictionary<int, List<int>> componentRootsToComponents, int vertCompRoot) {
List<int> compList;
if (componentRootsToComponents.TryGetValue(vertCompRoot, out compList)) {
foreach (var i in compList)
yield return i;
} else
yield return vertCompRoot;
}
void PutVerticalComponentIntoLayers(IEnumerable<int> vertComponent, int[] runningLayerCounts, bool[] alreadyInLayers) {
foreach (var i in vertComponent)
AddVertToLayers(i, runningLayerCounts, alreadyInLayers);
}
void AddVertToLayers(int i, int[] runningLayerCounts, bool[] alreadyInLayers) {
if (alreadyInLayers[i])
return;
int layerIndex = LayerArrays.Y[i];
int xIndex = runningLayerCounts[layerIndex];
var layer = LayerArrays.Layers[layerIndex];
layer[xIndex++] = i;
alreadyInLayers[i] = true;
List<int> block;
if (horizontalConstraints.BlockRootToBlock.TryGetValue(i, out block))
foreach (var v in block) {
if (alreadyInLayers[v]) continue;
layer[xIndex++] = v;
alreadyInLayers[v] = true;
}
runningLayerCounts[layerIndex] = xIndex;
}
static Dictionary<int, List<int>> CreateComponentRootsToComponentsMap(Dictionary<int, int> nodesToVerticalComponentsRoots) {
var d = new Dictionary<int, List<int>>();
foreach (var kv in nodesToVerticalComponentsRoots) {
int i = kv.Key;
var root = kv.Value;
List<int> component;
if (!d.TryGetValue(root, out component)) {
d[root] = component = new List<int>();
}
component.Add(i);
}
return d;
}
IEnumerable<IntPair> LiftLeftRightRelationsToComponentRoots(Dictionary<int, int> nodesToVerticalComponentsRoots) {
foreach (var pair in horizontalConstraints.LeftRighInts)
yield return new IntPair(GetFromDictionaryOrIdentical(nodesToVerticalComponentsRoots, pair.Item1),
GetFromDictionaryOrIdentical(nodesToVerticalComponentsRoots, pair.Item2));
foreach (var pair in horizontalConstraints.LeftRightIntNeibs)
yield return new IntPair(GetFromDictionaryOrIdentical(nodesToVerticalComponentsRoots, pair.Item1),
GetFromDictionaryOrIdentical(nodesToVerticalComponentsRoots, pair.Item2));
}
static int GetFromDictionaryOrIdentical(Dictionary<int, int> d, int key) {
int i;
if (d.TryGetValue(key, out i))
return i;
return key;
}
/// <summary>
/// These blocks are connected components in the vertical constraints. They don't necesserely span consequent layers.
/// </summary>
/// <returns></returns>
Dictionary<int, int> CreateVerticalComponents() {
var vertGraph = new BasicGraph<IntEdge>(from pair in horizontalConstraints.VerticalInts select new IntEdge(pair.Item1, pair.Item2));
var verticalComponents = ConnectedComponentCalculator<IntEdge>.GetComponents(vertGraph);
var nodesToComponentRoots = new Dictionary<int, int>();
foreach (var component in verticalComponents) {
var ca = component.ToArray();
if (ca.Length == 1)
continue;
int componentRoot = -1;
foreach (var j in component) {
if (componentRoot == -1)
componentRoot = j;
nodesToComponentRoots[j] = componentRoot;
}
}
return nodesToComponentRoots;
}
void RestoreState() {
LayerArrays.UpdateLayers(yetBestLayers);
}
// #if TEST_MSAGL
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledCode")]
// void Show() {
// SugiyamaLayoutSettings.ShowDatabase(database);
// }
// #endif
// #if TEST_MSAGL
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.Write(System.String)"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledCode")]
// static void PrintPositions(double[] positions) {
// for (int j = 0; j < positions.Length; j++)
// Console.Write(" " + positions[j]);
// Console.WriteLine();
// }
// #endif
void SortLayerBasedOnSolution(int[] layer, ISolverShell solver) {
int length = layer.Length;
var positions = new double[length];
int k = 0;
foreach (int v in layer)
positions[k++] = solver.GetVariableResolvedPosition(v);
Array.Sort(positions, layer);
int i = 0;
foreach (int v in layer)
LayerArrays.X[v] = i++;
}
const double ConstrainedVarWeight = 10e6;
const double PositionOverBaricenterWeight = 5;
static int NodeToBlockRootSoftOnLayerInfo(LayerInfo layerInfo, int node) {
int root;
return layerInfo.nodeToBlockRoot.TryGetValue(node, out root) ? root : node;
}
static void AddGoalToKeepFlatEdgesShortOnBlockLevel(LayerInfo layerInfo, ISolverShell solver) {
if (layerInfo != null)
foreach (var couple in layerInfo.flatEdges) {
int sourceBlockRoot = NodeToBlockRootSoftOnLayerInfo(layerInfo, couple.Item1);
int targetBlockRoot = NodeToBlockRootSoftOnLayerInfo(layerInfo, couple.Item2);
if (sourceBlockRoot != targetBlockRoot)
solver.AddGoalTwoVariablesAreClose(sourceBlockRoot, targetBlockRoot);
}
}
static bool NodeIsConstrainedBelow(int v, LayerInfo layerInfo) {
if (layerInfo == null)
return false;
return layerInfo.constrainedFromBelow.ContainsKey(v);
}
static bool NodeIsConstrainedAbove(int v, LayerInfo layerInfo) {
if (layerInfo == null)
return false;
return layerInfo.constrainedFromAbove.ContainsKey(v);
}
internal static bool BelongsToNeighbBlock(int p, LayerInfo layerInfo) {
return layerInfo != null && (layerInfo.nodeToBlockRoot.ContainsKey(p) || layerInfo.neigBlocks.ContainsKey(p));
//p is a root of the block
}
static bool NodesAreConstrainedBelow(int leftNode, int rightNode, LayerInfo layerInfo) {
return NodeIsConstrainedBelow(leftNode, layerInfo) && NodeIsConstrainedBelow(rightNode, layerInfo);
}
static bool NodesAreConstrainedAbove(int leftNode, int rightNode, LayerInfo layerInfo) {
return NodeIsConstrainedAbove(leftNode, layerInfo) && NodeIsConstrainedAbove(rightNode, layerInfo);
}
double GetGapFromNodeNodesConstrainedBelow(int leftNode, int rightNode, LayerInfo layerInfo,
int layerIndex) {
double gap = SimpleGapBetweenTwoNodes(leftNode, rightNode);
leftNode = layerInfo.constrainedFromBelow[leftNode];
rightNode = layerInfo.constrainedFromBelow[rightNode];
layerIndex--;
layerInfo = layerInfos[layerIndex];
if (layerIndex > 0 && NodesAreConstrainedBelow(leftNode, rightNode, layerInfo))
return Math.Max(gap, GetGapFromNodeNodesConstrainedBelow(leftNode, rightNode, layerInfo, layerIndex));
return Math.Max(gap, SimpleGapBetweenTwoNodes(leftNode, rightNode));
}
double GetGapFromNodeNodesConstrainedAbove(int leftNode, int rightNode, LayerInfo layerInfo,
int layerIndex) {
double gap = SimpleGapBetweenTwoNodes(leftNode, rightNode);
leftNode = layerInfo.constrainedFromAbove[leftNode];
rightNode = layerInfo.constrainedFromAbove[rightNode];
layerIndex++;
layerInfo = layerInfos[layerIndex];
if (layerIndex < LayerArrays.Layers.Length - 1 && NodesAreConstrainedAbove(leftNode, rightNode, layerInfo))
return Math.Max(gap, GetGapFromNodeNodesConstrainedAbove(leftNode, rightNode, layerInfo, layerIndex));
return Math.Max(gap, SimpleGapBetweenTwoNodes(leftNode, rightNode));
}
double SimpleGapBetweenTwoNodes(int leftNode, int rightNode) {
return database.anchors[leftNode].RightAnchor +
NodeSeparation() + database.anchors[rightNode].LeftAnchor;
}
internal static ISolverShell CreateSolver() {
return new SolverShell();
}
void PrepareProperLayeredGraphAndFillLayerInfos() {
layerInfos = new LayerInfo[NumberOfLayers];
CreateProperLayeredGraph();
CreateExtendedLayerArrays();
FillBlockRootToBlock();
FillLeftRightPairs();
FillFlatEdges();
FillAboveBelow();
FillBlockRootToVertConstrainedNode();
}
void FillBlockRootToVertConstrainedNode() {
foreach (LayerInfo layerInfo in layerInfos)
foreach (int v in VertConstrainedNodesOfLayer(layerInfo)) {
int blockRoot;
if (TryGetBlockRoot(v, out blockRoot, layerInfo))
layerInfo.blockRootToVertConstrainedNodeOfBlock[blockRoot] = v;
}
}
static bool TryGetBlockRoot(int v, out int blockRoot, LayerInfo layerInfo) {
if (layerInfo.nodeToBlockRoot.TryGetValue(v, out blockRoot))
return true;
if (layerInfo.neigBlocks.ContainsKey(v)) {
blockRoot = v;
return true;
}
return false;
}
static IEnumerable<int> VertConstrainedNodesOfLayer(LayerInfo layerInfo) {
if (layerInfo != null) {
foreach (int v in layerInfo.constrainedFromAbove.Keys)
yield return v;
foreach (int v in layerInfo.constrainedFromBelow.Keys)
yield return v;
}
}
void CreateExtendedLayerArrays() {
var layeringExt = new int[numberOfNodesOfProperGraph];
Array.Copy(initialLayering, layeringExt, initialLayering.Length);
foreach (IntEdge edge in ProperLayeredGraph.BaseGraph.Edges) {
var ledges = (LayerEdge[])edge.LayerEdges;
if (ledges != null && ledges.Length > 1) {
int layerIndex = initialLayering[edge.Source] - 1;
for (int i = 0; i < ledges.Length - 1; i++)
layeringExt[ledges[i].Target] = layerIndex--;
}
}
LayerArrays = new LayerArrays(layeringExt);
}
void CreateProperLayeredGraph() {
IEnumerable<IntEdge> edges = CreatePathEdgesOnIntGraph();
var nodeCount = Math.Max(intGraph.NodeCount, BasicGraph<Node, IntEdge>.VertexCount(edges));
var baseGraph = new BasicGraph<Node, IntEdge>(edges, nodeCount) { Nodes = intGraph.Nodes };
ProperLayeredGraph = new ProperLayeredGraph(baseGraph);
}
IEnumerable<IntEdge> CreatePathEdgesOnIntGraph() {
numberOfNodesOfProperGraph = intGraph.NodeCount;
var ret = new List<IntEdge>();
foreach (IntEdge ie in intGraph.Edges) {
if (initialLayering[ie.Source] > initialLayering[ie.Target]) {
CreateLayerEdgesUnderIntEdge(ie);
ret.Add(ie);
if (horizontalConstraints.VerticalInts.Contains(new Tuple<int, int>(ie.Source, ie.Target)))
verticalEdges.Add(ie);
}
}
return ret;
}
void CreateLayerEdgesUnderIntEdge(IntEdge ie) {
int source = ie.Source;
int target = ie.Target;
int span = LayeredLayoutEngine.EdgeSpan(initialLayering, ie);
ie.LayerEdges = new LayerEdge[span];
Debug.Assert(span > 0);
if (span == 1)
ie.LayerEdges[0] = new LayerEdge(ie.Source, ie.Target, ie.CrossingWeight);
else {
ie.LayerEdges[0] = new LayerEdge(source, numberOfNodesOfProperGraph, ie.CrossingWeight);
for (int i = 0; i < span - 2; i++)
ie.LayerEdges[i + 1] = new LayerEdge(numberOfNodesOfProperGraph++, numberOfNodesOfProperGraph,
ie.CrossingWeight);
ie.LayerEdges[span - 1] = new LayerEdge(numberOfNodesOfProperGraph++, target, ie.CrossingWeight);
}
}
void FillAboveBelow() {
foreach (IntEdge ie in verticalEdges) {
foreach (LayerEdge le in ie.LayerEdges) {
int upper = le.Source;
int lower = le.Target;
RegisterAboveBelowOnConstrainedUpperLower(upper, lower);
}
}
foreach (var p in horizontalConstraints.VerticalInts)
RegisterAboveBelowOnConstrainedUpperLower(p.Item1, p.Item2);
}
void RegisterAboveBelowOnConstrainedUpperLower(int upper, int lower) {
LayerInfo topLayerInfo = GetOrCreateLayerInfo(LayerArrays.Y[upper]);
LayerInfo bottomLayerInfo = GetOrCreateLayerInfo(LayerArrays.Y[lower]);
topLayerInfo.constrainedFromBelow[upper] = lower;
bottomLayerInfo.constrainedFromAbove[lower] = upper;
}
void FillFlatEdges() {
foreach (IntEdge edge in intGraph.Edges) {
int l = initialLayering[edge.Source];
if (l == initialLayering[edge.Target]) {
GetOrCreateLayerInfo(l).flatEdges.Insert(new Tuple<int, int>(edge.Source, edge.Target));
}
}
}
void FillLeftRightPairs() {
foreach (var p in horizontalConstraints.LeftRighInts) {
LayerInfo layerInfo = GetOrCreateLayerInfo(initialLayering[p.Item1]);
layerInfo.leftRight.Insert(p);
}
}
/// <summary>
/// when we call this function we know that a LayerInfo is needed
/// </summary>
/// <param name="layerNumber"></param>
/// <returns></returns>
LayerInfo GetOrCreateLayerInfo(int layerNumber) {
LayerInfo layerInfo = layerInfos[layerNumber] ?? (layerInfos[layerNumber] = new LayerInfo());
return layerInfo;
}
void FillBlockRootToBlock() {
foreach (var p in horizontalConstraints.BlockRootToBlock) {
LayerInfo layerInfo = GetOrCreateLayerInfo(initialLayering[p.Key]);
layerInfo.neigBlocks[p.Key] = p.Value;
foreach (int i in p.Value)
layerInfo.nodeToBlockRoot[i] = p.Key;
}
}
}
}