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

225 lines
No EOL
9.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Msagl.Core;
using Microsoft.Msagl.Core.DataStructures;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.GraphAlgorithms;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.ProjectionSolver;
using Microsoft.Msagl.Routing;
namespace Microsoft.Msagl.Layout.Layered {
internal class TwoLayerFlatEdgeRouter : AlgorithmBase {
readonly int[] bottomLayer;
InteractiveEdgeRouter interactiveEdgeRouter;
double[] labelCenters;
Dictionary<Label, ICurve> labelsToLabelObstacles = new Dictionary<Label, ICurve>();
IntPair[] pairArray;
readonly Routing routing;
readonly int[] topLayer;
private SugiyamaLayoutSettings settings;
internal TwoLayerFlatEdgeRouter(SugiyamaLayoutSettings settings, Routing routing, int[] bottomLayer, int[] topLayer)
{
this.settings = settings;
this.topLayer = topLayer;
this.bottomLayer = bottomLayer;
this.routing = routing;
InitLabelsInfo();
}
Database Database {
get { return routing.Database; }
}
int[] Layering {
get { return routing.LayerArrays.Y; }
}
BasicGraph<IntEdge> IntGraph {
get { return routing.IntGraph; }
}
LayerArrays LayerArrays {
get { return routing.LayerArrays; }
}
double PaddingForEdges {
get { return settings.LayerSeparation / 8; }
}
void InitLabelsInfo() {
pairArray = new Set<IntPair>(from v in bottomLayer
where v < IntGraph.NodeCount
from edge in IntGraph.OutEdges(v)
where edge.Source != edge.Target
where Layering[edge.Target] == Layering[edge.Source]
select new IntPair(edge.Source, edge.Target)).ToArray();
labelCenters = new double[pairArray.Length];
int i = 0;
foreach (IntPair p in pairArray) {
int leftNode, rightNode;
if (LayerArrays.X[p.First] < LayerArrays.X[p.Second]) {
leftNode = p.First;
rightNode = p.Second;
} else {
leftNode = p.Second;
rightNode = p.First;
}
labelCenters[i++] = (Database.Anchors[leftNode].Right + Database.Anchors[rightNode].Left)/2;
//labelCenters contains ideal position for nodes at the moment
}
InitLabelsToLabelObstacles();
}
void InitLabelsToLabelObstacles() {
labelsToLabelObstacles = new Dictionary<Label, ICurve>();
IEnumerable<Label> labels = from p in pairArray from label in PairLabels(p) select label;
foreach (Label label in labels)
labelsToLabelObstacles[label] = CreatObstaceOnLabel(label);
}
double GetMaxLabelWidth(IntPair intPair) {
IEnumerable<Label> multiEdgeLabels = PairLabels(intPair);
if (multiEdgeLabels.Any())
return multiEdgeLabels.Max(label => label.Width);
return 0;
}
IEnumerable<Label> PairLabels(IntPair intPair) {
return from edge in Database.GetMultiedge(intPair)
let label = edge.Edge.Label
where label != null
select label;
}
/// <summary>
/// Executes the algorithm.
/// </summary>
protected override void RunInternal() {
if (pairArray.Length > 0) {
PositionLabelsOfFlatEdges();
interactiveEdgeRouter = new InteractiveEdgeRouter(GetObstacles(), PaddingForEdges, PaddingForEdges/3, Math.PI/6);
interactiveEdgeRouter.CalculateWholeTangentVisibilityGraph();
foreach (IntEdge intEdge in IntEdges())
{
this.ProgressStep();
RouteEdge(intEdge);
}
}
}
IEnumerable<ICurve> GetObstacles() {
return (from v in topLayer.Concat(bottomLayer)
where v < routing.OriginalGraph.Nodes.Count
select routing.IntGraph.Nodes[v].BoundaryCurve).Concat(LabelCurves());
}
IEnumerable<ICurve> LabelCurves() {
return from edge in IntEdges()
let label = edge.Edge.Label
where label != null
select CreatObstaceOnLabel(label);
}
static ICurve CreatObstaceOnLabel(Label label) {
var c = new Curve();
double obstacleBottom = label.Center.Y - label.Height/4;
c.AddSegment(new LineSegment(new Point(label.BoundingBox.Left, obstacleBottom),
new Point(label.BoundingBox.Right, obstacleBottom)));
Curve.ContinueWithLineSegment(c, label.BoundingBox.RightTop);
Curve.ContinueWithLineSegment(c, label.BoundingBox.LeftTop);
Curve.CloseCurve(c);
return c;
}
IEnumerable<IntEdge> IntEdges() {
return from pair in pairArray from edge in Database.GetMultiedge(pair) select edge;
}
void RouteEdge(IntEdge edge) {
if (edge.HasLabel)
RouteEdgeWithLabel(edge, edge.Edge.Label);
else
RouteEdgeWithNoLabel(edge);
}
void RouteEdgeWithLabel(IntEdge intEdge, Label label) {
//we allow here for the edge to cross its own label
Node sourceNode = routing.IntGraph.Nodes[intEdge.Source];
Node targetNode = routing.IntGraph.Nodes[intEdge.Target];
var sourcePort = new FloatingPort(sourceNode.BoundaryCurve, sourceNode.Center);
var targetPort = new FloatingPort(targetNode.BoundaryCurve, targetNode.Center);
ICurve labelObstacle = labelsToLabelObstacles[label];
var labelPort = new FloatingPort(labelObstacle, label.Center);
SmoothedPolyline poly0;
interactiveEdgeRouter.RouteSplineFromPortToPortWhenTheWholeGraphIsReady(sourcePort, labelPort, true, out poly0);
SmoothedPolyline poly1;
interactiveEdgeRouter.RouteSplineFromPortToPortWhenTheWholeGraphIsReady(labelPort, targetPort, true, out poly1);
Site site = poly1.HeadSite.Next;
Site lastSite = poly0.LastSite;
lastSite.Next = site;
site.Previous = lastSite;
var eg = intEdge.Edge.EdgeGeometry;
eg.SetSmoothedPolylineAndCurve(poly0);
Arrowheads.TrimSplineAndCalculateArrowheads(eg, intEdge.Edge.Source.BoundaryCurve,
intEdge.Edge.Target.BoundaryCurve, eg.Curve, false,
settings.EdgeRoutingSettings.KeepOriginalSpline);
}
void RouteEdgeWithNoLabel(IntEdge intEdge) {
Node sourceNode = routing.IntGraph.Nodes[intEdge.Source];
Node targetNode = routing.IntGraph.Nodes[intEdge.Target];
var sourcePort = new FloatingPort(sourceNode.BoundaryCurve, sourceNode.Center);
var targetPort = new FloatingPort(targetNode.BoundaryCurve, targetNode.Center);
var eg = intEdge.Edge.EdgeGeometry;
SmoothedPolyline sp;
eg.Curve = interactiveEdgeRouter.RouteSplineFromPortToPortWhenTheWholeGraphIsReady(sourcePort, targetPort, true, out sp);
Arrowheads.TrimSplineAndCalculateArrowheads(eg, intEdge.Edge.Source.BoundaryCurve,
intEdge.Edge.Target.BoundaryCurve, eg.Curve, false,
settings.EdgeRoutingSettings.KeepOriginalSpline);
intEdge.Edge.EdgeGeometry = eg;
}
void PositionLabelsOfFlatEdges() {
if (labelCenters == null || labelCenters.Length == 0)
return;
SortLabelsByX();
CalculateLabelsX();
}
void CalculateLabelsX() {
int i;
ISolverShell solver = ConstrainedOrdering.CreateSolver();
for (i = 0; i < pairArray.Length; i++)
solver.AddVariableWithIdealPosition(i, labelCenters[i], GetLabelWeight(pairArray[i]));
//add non overlapping constraints between to neighbor labels
double prevLabelWidth = GetMaxLabelWidth(pairArray[0]);
for (i = 0; i < pairArray.Length - 1; i++)
solver.AddLeftRightSeparationConstraint(i, i + 1,
(prevLabelWidth +
(prevLabelWidth = GetMaxLabelWidth(pairArray[i + 1])))/2 +
settings.NodeSeparation);
for (i = 0; i < labelCenters.Length; i++) {
double x = labelCenters[i] = solver.GetVariableResolvedPosition(i);
foreach (Label label in PairLabels(pairArray[i]))
label.Center = new Point(x, label.Center.Y);
}
}
double GetLabelWeight(IntPair intPair) {
return Database.GetMultiedge(intPair).Count;
}
void SortLabelsByX() {
Array.Sort(labelCenters, pairArray);
}
}
}