Aetheria/Assets/Plugins/MSAGL/Routing/Spline/Bundling/PathFixer.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

228 lines
9.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Msagl.Core.DataStructures;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Geometry.Curves;
namespace Microsoft.Msagl.Routing.Spline.Bundling {
internal class PathFixer {
MetroGraphData metroGraphData;
Func<Metroline, Point, bool> polylineAcceptsPoint;
Set<Point> foundCrossings = new Set<Point>();
Set<Point> crossingsThatShouldBecomeHubs = new Set<Point>();
Set<Point> pointsToDelete;
public PathFixer(MetroGraphData metroGraphData, Func<Metroline, Point, bool> polylineAcceptsPoint) {
this.metroGraphData = metroGraphData;
this.polylineAcceptsPoint = polylineAcceptsPoint;
}
IEnumerable<PolylinePoint> Vertices() {
return metroGraphData.Edges.Select(e => (Polyline)e.Curve).SelectMany(p => p.PolylinePoints);
}
IEnumerable<Polyline> Polylines { get { return metroGraphData.Edges.Select(e => (Polyline)e.Curve); } }
IEnumerable<PointPair> Edges() {
var set = new Set<PointPair>();
foreach (var poly in Polylines)
for (var pp = poly.StartPoint; pp.Next != null; pp = pp.Next)
set.Insert(FlipCollapser.OrderedPair(pp));
return set;
}
internal bool Run() {
if (metroGraphData.Edges.Count() == 0) return false;
var splittingPoints = new Dictionary<PointPair, List<Point>>();
var treeOfVertices = new RTree<Point>();
foreach (var vertex in Vertices()) {
var r = new Rectangle(vertex.Point);
r.Pad(ApproximateComparer.IntersectionEpsilon);
treeOfVertices.Add(r, vertex.Point);
}
var treeOfEdges = RectangleNode<PointPair>.CreateRectangleNodeOnData(Edges(), e => new Rectangle(e.First, e.Second));
RectangleNodeUtils.CrossRectangleNodes<PointPair>(treeOfEdges, treeOfEdges, (a, b) => IntersectTwoEdges(a, b, splittingPoints, treeOfVertices));
SortInsertedPoints(splittingPoints);
bool pointsInserted = InsertPointsIntoPolylines(splittingPoints);
bool progress = FixPaths();
bool pointsRemoved = RemoveUnimportantCrossings();
return progress || pointsInserted || pointsRemoved;
}
bool FixPaths() {
bool progress = false;
if (RemoveSelfCycles()) progress = true;
//if (CollapseCycles()) progress = true;
if (ReduceEdgeCrossings()) progress = true;
return progress;
}
void SortInsertedPoints(Dictionary<PointPair, List<Point>> splittingPoints) {
foreach (var pair in splittingPoints)
SortInsideSegment(pair.Key, pair.Value);
}
void SortInsideSegment(PointPair edge, List<Point> list) {
System.Diagnostics.Debug.Assert(list.Count > 0, "an edge should not be present with an empty list");
list.Sort((a, b) => (a - edge.First).Length.CompareTo((b - edge.First).Length));
}
bool InsertPointsIntoPolylines(Dictionary<PointPair, List<Point>> splittingPoints) {
bool inserted = false;
foreach (var metroline in metroGraphData.Metrolines) {
if (InsertPointsIntoPolyline(metroline, splittingPoints))
inserted = true;
}
return inserted;
}
bool InsertPointsIntoPolyline(Metroline metroline, Dictionary<PointPair, List<Point>> splittingPoints) {
bool inserted = false;
for (var pp = metroline.Polyline.StartPoint; pp.Next != null; pp = pp.Next)
if (InsertPointsOnPolypoint(pp, splittingPoints, metroline)) inserted = true;
return inserted;
}
bool InsertPointsOnPolypoint(PolylinePoint pp, Dictionary<PointPair, List<Point>> splittingPoints, Metroline metroline) {
var pointPair = FlipCollapser.OrderedPair(pp);
var reversed = pp.Point != pointPair.First;
List<Point> list;
if (!splittingPoints.TryGetValue(pointPair, out list))
return false;
var endPolyPoint = pp.Next;
var poly = pp.Polyline;
if (reversed)
for (int i = list.Count - 1; i >= 0; i--) {
if ( polylineAcceptsPoint!=null && !polylineAcceptsPoint(metroline, list[i])) continue;
var p = new PolylinePoint(list[i]) { Prev = pp, Polyline = poly };
pp.Next = p;
pp = p;
}
else
for (int i = 0; i < list.Count; i++) {
if (polylineAcceptsPoint!=null &&!polylineAcceptsPoint(metroline, list[i])) continue;
var p = new PolylinePoint(list[i]) { Prev = pp, Polyline = poly };
pp.Next = p;
pp = p;
}
pp.Next = endPolyPoint;
endPolyPoint.Prev = pp;
return true;
}
bool RemoveSelfCycles() {
bool progress = false;
foreach (var poly in Polylines)
if (RemoveSelfCyclesFromPolyline(poly)) progress = true;
return progress;
}
//returns removed points
internal static bool RemoveSelfCyclesFromPolyline(Polyline poly) {
bool progress = false;
Dictionary<Point, PolylinePoint> pointsToPp = new Dictionary<Point, PolylinePoint>();
for (var pp = poly.StartPoint; pp != null; pp = pp.Next) {
var point = pp.Point;
PolylinePoint previous;
if (pointsToPp.TryGetValue(point, out previous)) {//we have a cycle
for (var px = previous.Next; px != pp.Next; px = px.Next) {
pointsToPp.Remove(px.Point);
}
previous.Next = pp.Next;
pp.Next.Prev = previous;
progress = true;
}
else
pointsToPp[pp.Point] = pp;
}
return progress;
}
//bool CollapseCycles() {
// var cycleCollapser = new FlipCollapser(metroGraphData, bundlingSettings, cdt);
// cycleCollapser.Run();
// crossingsThatShouldBecomeHubs.InsertRange(cycleCollapser.GetChangedCrossing());
// //TimeMeasurer.DebugOutput("#crossingsThatShouldBecomeHubs = " + crossingsThatShouldBecomeHubs.Count);
// return false;
//}
bool ReduceEdgeCrossings() {
var cycleCollapser = new FlipSwitcher(metroGraphData);
cycleCollapser.Run();
crossingsThatShouldBecomeHubs.InsertRange(cycleCollapser.GetChangedHubs());
//TimeMeasurer.DebugOutput("#reduced crossings = " + cycleCollapser.NumberOfReducedCrossings());
return cycleCollapser.NumberOfReducedCrossings() > 0;
}
bool RemoveUnimportantCrossings() {
bool removed = false;
pointsToDelete = foundCrossings - crossingsThatShouldBecomeHubs;
foreach (var polyline in Polylines)
if (RemoveUnimportantCrossingsFromPolyline(polyline)) removed = true;
return removed;
}
bool RemoveUnimportantCrossingsFromPolyline(Polyline polyline) {
bool removed = false;
for (var p = polyline.StartPoint.Next; p != null && p.Next != null; p = p.Next)
if (pointsToDelete.Contains(p.Point) && Point.GetTriangleOrientation(p.Prev.Point, p.Point, p.Next.Point) == TriangleOrientation.Collinear) {
//forget p
var pp = p.Prev;
var pn = p.Next;
pp.Next = pn;
pn.Prev = pp;
p = pp;
removed = true;
}
return removed;
}
void IntersectTwoEdges(PointPair a, PointPair b, Dictionary<PointPair, List<Point>> splittingPoints, RTree<Point> tree) {
Point x;
if (LineSegment.Intersect(a.First, a.Second, b.First, b.Second, out x)) {
Point vertex = FindExistingVertexOrCreateNew(tree, x);
if (AddVertexToSplittingList(a, splittingPoints, vertex) |
AddVertexToSplittingList(b, splittingPoints, vertex))
foundCrossings.Insert(vertex);
}
}
Point FindExistingVertexOrCreateNew(RTree<Point> tree, Point x) {
var p = tree.RootNode.FirstHitNode(x);
if (p != null)
return p.UserData;
var rect = new Rectangle(x);
rect.Pad(ApproximateComparer.IntersectionEpsilon);
tree.Add(rect, x);
return x;
}
bool AddVertexToSplittingList(PointPair a, Dictionary<PointPair, List<Point>> splittingPoints, Point intersectionPoint) {
#if DEBUG && TEST_MSAGL
double t;
System.Diagnostics.Debug.Assert(Point.DistToLineSegment(intersectionPoint, a.First, a.Second, out t) < ApproximateComparer.IntersectionEpsilon);
#endif
if (!ApproximateComparer.CloseIntersections(intersectionPoint, a.First) &&
!ApproximateComparer.CloseIntersections(intersectionPoint, a.Second)) {
List<Point> list;
if (!splittingPoints.TryGetValue(a, out list))
splittingPoints[a] = list = new List<Point>();
if (!list.Contains(intersectionPoint)) {
list.Add(intersectionPoint);
return true;
}
}
return false;
}
}
}