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
994 lines
44 KiB
C#
994 lines
44 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using Microsoft.Msagl.Core.DataStructures;
|
|
using Microsoft.Msagl.Core.Geometry;
|
|
using Microsoft.Msagl.Core.Geometry.Curves;
|
|
using Microsoft.Msagl.Core.Layout;
|
|
using Microsoft.Msagl.Core.ProjectionSolver;
|
|
using Microsoft.Msagl.DebugHelpers;
|
|
using Microsoft.Msagl.Routing.Visibility;
|
|
|
|
namespace Microsoft.Msagl.Routing.Rectilinear.Nudging {
|
|
/// <summary>
|
|
/// following paper "Orthogonal Connector Routing" which is included in the project
|
|
/// </summary>
|
|
#if TEST_MSAGL
|
|
public
|
|
#else
|
|
internal
|
|
#endif
|
|
class Nudger {
|
|
|
|
bool HasGroups {
|
|
get { return (null != HierarchyOfGroups) && (HierarchyOfGroups.Count > 0); }
|
|
}
|
|
|
|
Dictionary<AxisEdge, Polyline> axisEdgesToObstaclesTheyOriginatedFrom;
|
|
|
|
List<Path> Paths { get; set; }
|
|
|
|
IEnumerable<Polyline> Obstacles { get; set; }
|
|
internal VisibilityGraph PathVisibilityGraph { get; set; }
|
|
|
|
/// <summary>
|
|
/// "nudge" paths to decrease the number of intersections and stores the results inside WidePaths of "paths"
|
|
/// </summary>
|
|
/// <param name="paths">paths through the graph</param>
|
|
/// <param name="cornerFitRad">two parallel paths should be separated by this distance if it is feasible</param>
|
|
/// <param name="obstacles">polygonal convex obstacles organized in a tree; the obstacles here are padded original obstacles</param>
|
|
/// <param name="ancestorsSets"></param>
|
|
/// <returns></returns>
|
|
internal Nudger(IEnumerable<Path> paths, double cornerFitRad, IEnumerable<Polyline> obstacles,
|
|
Dictionary<Shape, Set<Shape>> ancestorsSets) {
|
|
AncestorsSets = ancestorsSets;
|
|
HierarchyOfGroups = RectangleNode<Shape>.CreateRectangleNodeOnEnumeration(
|
|
ancestorsSets.Keys.Where(shape => shape.IsGroup).Select(group => new RectangleNode<Shape>(group, group.BoundingBox)));
|
|
Obstacles = obstacles;
|
|
EdgeSeparation = 2 * cornerFitRad;
|
|
Paths = new List<Path>(paths);
|
|
HierarchyOfObstacles =
|
|
RectangleNode<Polyline>.CreateRectangleNodeOnEnumeration(
|
|
obstacles.Select(p => new RectangleNode<Polyline>(p, p.BoundingBox)));
|
|
MapPathsToTheirObstacles();
|
|
}
|
|
|
|
Dictionary<Shape, Set<Shape>> AncestorsSets { get; set; }
|
|
|
|
void MapPathsToTheirObstacles() {
|
|
PathToObstacles = new Dictionary<Path, Tuple<Polyline, Polyline>>();
|
|
foreach (var path in Paths)
|
|
MapPathToItsObstacles(path);
|
|
}
|
|
|
|
void MapPathToItsObstacles(Path path) {
|
|
var startNode = HierarchyOfObstacles.FirstHitNode(path.PathPoints.First(),ObstacleTest);
|
|
var endNode = HierarchyOfObstacles.FirstHitNode(path.PathPoints.Last(), ObstacleTest);
|
|
if ((null != startNode) && (null != endNode)) {
|
|
PathToObstacles[path] = new Tuple<Polyline, Polyline>(startNode.UserData, endNode.UserData);
|
|
}
|
|
}
|
|
|
|
static HitTestBehavior ObstacleTest(Point pnt, Polyline polyline) {
|
|
return (Curve.PointRelativeToCurveLocation(pnt, polyline) !=
|
|
PointLocation.Outside)
|
|
? HitTestBehavior.Stop
|
|
: HitTestBehavior.Continue;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
protected RectangleNode<Polyline> HierarchyOfObstacles { get; set; }
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
protected RectangleNode<Shape> HierarchyOfGroups { get; set; }
|
|
|
|
internal void Calculate(Directions direction, bool mergePaths) {
|
|
NudgingDirection = direction;
|
|
PathRefiner.RefinePaths(Paths, mergePaths);
|
|
// ShowPathsDebug(Paths);
|
|
GetPathOrdersAndPathGraph();
|
|
MapAxisEdgesToTheirObstacles();
|
|
DrawPaths();
|
|
//ShowPathsDebug(Paths);
|
|
}
|
|
|
|
void MapAxisEdgesToTheirObstacles() {
|
|
axisEdgesToObstaclesTheyOriginatedFrom = new Dictionary<AxisEdge, Polyline>();
|
|
foreach (var path in Paths)
|
|
MapPathEndAxisEdgesToTheirObstacles(path);
|
|
|
|
//The assignment above was too greedy. An edge belonging to interiour edges of some path can be marked by mistake.
|
|
foreach (var path in Paths)
|
|
UmmapPathInteriourFromStrangerObstacles(path);
|
|
}
|
|
|
|
void UmmapPathInteriourFromStrangerObstacles(Path path) {
|
|
var firstUnmappedEdge = FindFirstUnmappedEdge(path);
|
|
if(firstUnmappedEdge==null) return;
|
|
var lastUnmappedEdge = FindLastUnmappedEdge(path);
|
|
for (var edge = firstUnmappedEdge; edge != null && edge != lastUnmappedEdge; edge = edge.Next)
|
|
axisEdgesToObstaclesTheyOriginatedFrom.Remove(edge.AxisEdge);
|
|
}
|
|
|
|
PathEdge FindLastUnmappedEdge(Path path) {
|
|
for (var edge = path.LastEdge; edge != null; edge = edge.Prev)
|
|
if (edge.AxisEdge.Direction != NudgingDirection)
|
|
return edge;
|
|
return null;
|
|
}
|
|
|
|
PathEdge FindFirstUnmappedEdge(Path path) {
|
|
for (var edge = path.FirstEdge; edge != null; edge = edge.Next)
|
|
if (edge.AxisEdge.Direction != NudgingDirection)
|
|
return edge;
|
|
return null;
|
|
}
|
|
|
|
void MapPathEndAxisEdgesToTheirObstacles(Path path) {
|
|
Tuple<Polyline, Polyline> coupleOfObstacles;
|
|
if (PathToObstacles.TryGetValue(path, out coupleOfObstacles)) {
|
|
ProcessThePathStartToMapAxisEdgesToTheirObstacles(path, coupleOfObstacles.Item1);
|
|
ProcessThePathEndToMapAxisEdgesToTheirObstacles(path, coupleOfObstacles.Item2);
|
|
}
|
|
}
|
|
|
|
void ProcessThePathEndToMapAxisEdgesToTheirObstacles(Path path, Polyline endPolyline) {
|
|
for (var edge = path.LastEdge;
|
|
edge != null && CompassVector.DirectionsAreParallel(edge.Direction, NudgingDirection);
|
|
edge = edge.Prev)
|
|
axisEdgesToObstaclesTheyOriginatedFrom[edge.AxisEdge] = endPolyline;
|
|
}
|
|
|
|
void ProcessThePathStartToMapAxisEdgesToTheirObstacles(Path path, Polyline startPolyline) {
|
|
for (var edge = path.FirstEdge;
|
|
edge != null && CompassVector.DirectionsAreParallel(edge.Direction, NudgingDirection);
|
|
edge = edge.Next)
|
|
axisEdgesToObstaclesTheyOriginatedFrom[edge.AxisEdge] = startPolyline;
|
|
//possible bug here because an edge might ignore two obstacles if it connects them
|
|
}
|
|
|
|
|
|
|
|
void GetPathOrdersAndPathGraph() {
|
|
var combinatorialNudger = new CombinatorialNudger(Paths);
|
|
PathOrders = combinatorialNudger.GetOrder();
|
|
PathVisibilityGraph = combinatorialNudger.PathVisibilityGraph;
|
|
}
|
|
|
|
/*
|
|
[Conditional("DEBUG")]
|
|
void CheckPathsForSwitchbacks() {
|
|
foreach(var p in Paths)
|
|
CheckForSwitchbacks(p.PathPoints.ToArray());
|
|
}
|
|
*/
|
|
|
|
|
|
Directions NudgingDirection { get; set; }
|
|
|
|
#region debugging
|
|
#if TEST_MSAGL
|
|
static internal ICurve[] GetCurvesForShow(IEnumerable<Path> paths, IEnumerable<Polyline> obstacles) {
|
|
var ret = new List<ICurve>();
|
|
foreach (var path in paths) {
|
|
var poly = new Polyline();
|
|
foreach (var point in path.PathPoints)
|
|
poly.AddPoint(point);
|
|
ret.Add(poly);
|
|
}
|
|
ret.AddRange(obstacles.Cast<ICurve>());
|
|
return ret.ToArray();
|
|
}
|
|
#endif
|
|
#endregion
|
|
void DrawPaths() {
|
|
SetWidthsOfArrowheads();
|
|
CreateLongestNudgedSegments();
|
|
FindFreeSpaceInDirection(PathVisibilityGraph.Edges.Cast<AxisEdge>());
|
|
MoveLongestSegsIdealPositionsInsideFeasibleIntervals();
|
|
PositionShiftedEdges();
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetWidthsOfArrowheads() {
|
|
foreach (Path edgePath in Paths)
|
|
SetWidthsOfArrowheadsForEdge(edgePath);
|
|
}
|
|
|
|
static void SetWidthsOfArrowheadsForEdge(Path path) {
|
|
var edgeGeom = path.EdgeGeometry;
|
|
if (edgeGeom.TargetArrowhead != null) {
|
|
PathEdge pathEdge = path.LastEdge;
|
|
pathEdge.Width = Math.Max(edgeGeom.TargetArrowhead.Width, pathEdge.Width);
|
|
}
|
|
if (edgeGeom.SourceArrowhead != null) {
|
|
PathEdge pathEdge = path.FirstEdge;
|
|
pathEdge.Width = Math.Max(edgeGeom.SourceArrowhead.Width, pathEdge.Width);
|
|
}
|
|
}
|
|
|
|
internal double EdgeSeparation { get; set; }
|
|
|
|
void PositionShiftedEdges() {
|
|
//we are using 2*cornerFitRadius for the minimal edge separation
|
|
Solver = new UniformOneDimensionalSolver(EdgeSeparation);
|
|
foreach (var segment in LongestNudgedSegs)
|
|
CreateVariablesOfLongestSegment(segment);
|
|
CreateConstraintsOfTheOrder();
|
|
CreateConstraintsBetweenLongestSegments();
|
|
Solver.Solve();
|
|
ShiftPathEdges();
|
|
}
|
|
|
|
void MoveLongestSegsIdealPositionsInsideFeasibleIntervals() {
|
|
foreach (var seg in LongestNudgedSegs)
|
|
MoveLongestSegIdealPositionsInsideFeasibleInterval(seg);
|
|
}
|
|
|
|
static void MoveLongestSegIdealPositionsInsideFeasibleInterval(LongestNudgedSegment seg) {
|
|
if (seg.IsFixed) return;
|
|
var leftBound = seg.GetLeftBound();
|
|
var rightBound = seg.GetRightBound();
|
|
if (seg.IdealPosition < leftBound)
|
|
seg.IdealPosition=leftBound;
|
|
else if( seg.IdealPosition> rightBound)
|
|
seg.IdealPosition=rightBound;
|
|
}
|
|
|
|
void ShiftPathEdges() {
|
|
foreach (var path in Paths)
|
|
path.PathPoints = GetShiftedPoints(path).ToArray();
|
|
}
|
|
|
|
IEnumerable<Point> GetShiftedPoints(Path path) {
|
|
return RemoveSwitchbacksAndMiddlePoints(GetShiftedPointsSimple(path));
|
|
}
|
|
/// <summary>
|
|
/// sometimes we have very small mistakes in the positions that have to be fixed
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
static Point Rectilinearise(Point a, Point b) {
|
|
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369 there are no structs in js
|
|
b = b.Clone();
|
|
#endif
|
|
if (a.X != b.X && a.Y != b.Y) {
|
|
var dx = Math.Abs(a.X - b.X);
|
|
var dy = Math.Abs(a.Y - b.Y);
|
|
if (dx < dy)
|
|
b.X = a.X;
|
|
else
|
|
b.Y = a.Y;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
IEnumerable<Point> GetShiftedPointsSimple(Path path) {
|
|
var edge = path.FirstEdge;
|
|
yield return ShiftedPoint(edge.Source, edge.LongestNudgedSegment);
|
|
foreach (var e in path.PathEdges)
|
|
yield return ShiftedEdgePositionOfTarget(e);
|
|
}
|
|
|
|
Point ShiftedEdgePositionOfTarget(PathEdge e) {
|
|
return e.LongestNudgedSegment != null || e.Next == null
|
|
? ShiftedPoint(e.Target, e.LongestNudgedSegment)
|
|
: ShiftedPoint(e.Next.Source, e.Next.LongestNudgedSegment);
|
|
}
|
|
|
|
Point ShiftedPoint(Point point, LongestNudgedSegment segment) {
|
|
if (segment == null)
|
|
return point;
|
|
var t = Solver.GetVariablePosition(segment.Id);
|
|
return NudgingDirection == Directions.North ? new Point(t, point.Y) : new Point(point.X, -t);
|
|
}
|
|
|
|
#region debug
|
|
// #if TEST_MSAGL
|
|
// internal static void ShowPathsFromPoints(IEnumerable<Path> paths, IEnumerable<Polyline> enumerable) {
|
|
// var dd = new List<DebugCurve>();
|
|
// if (enumerable != null)
|
|
// dd.AddRange(GetObstacleBoundaries(enumerable, "grey"));
|
|
// int i = 0;
|
|
// foreach (var p in paths)
|
|
// dd.AddRange(PathDebugCurvesFromPoints(p, DebugCurve.Colors[Math.Min(DebugCurve.Colors.Length, i++)]));
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(dd);
|
|
// }
|
|
//
|
|
// static IEnumerable<DebugCurve> PathDebugCurvesFromPoints(Path path, string color) {
|
|
// const double startWidth = 0.01;
|
|
// const double endWidth = 3;
|
|
//
|
|
// var pts = path.PathPoints.ToArray();
|
|
// double delta = (endWidth - startWidth) / (pts.Length - 1);
|
|
// for (int i = 0; i < pts.Length - 1; i++)
|
|
// yield return new DebugCurve(startWidth + delta * i, color, new LineSegment(pts[i], pts[i + 1]));
|
|
// }
|
|
//
|
|
// internal static void ShowParamPaths( Point s, Point e, params Path[] paths ) {
|
|
// ShowOrderedPaths(null,paths,s,e);
|
|
// }
|
|
//
|
|
// // ReSharper disable UnusedMember.Local
|
|
// internal static void ShowOrderedPaths(IEnumerable<Polyline> obstacles, IEnumerable<Path> paths, Point s, Point e) {
|
|
// // ReSharper restore UnusedMember.Local
|
|
// string[] colors = { "red", "green", "blue", "violet", "rose", "black" };
|
|
//
|
|
// const double startWidth = 0.001;
|
|
// const double endWidth = 0.1;
|
|
// var dd = new List<DebugCurve>();
|
|
// if (obstacles != null)
|
|
// dd.AddRange(GetObstacleBoundaries(obstacles, "grey"));
|
|
// int i = 0;
|
|
// foreach (var path in paths)
|
|
// dd.AddRange(GetTestPathAsDebugCurve(startWidth, endWidth, colors[Math.Min(colors.Length - 1, i++)], path));
|
|
//
|
|
// var ell = new DebugCurve(1, "black", new Ellipse(0.01, 0.01, s));
|
|
// dd.Add(ell);
|
|
// dd.Add(new DebugCurve(1, "black", new Ellipse(0.02, 0.02, e)));
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(dd.Concat(GetObstacleBoundaries(obstacles, "lightblue")));
|
|
// }
|
|
//
|
|
// static IEnumerable<DebugCurve> GetTestPathAsDebugCurve(double startWidth, double endWidth, string color, Path path) {
|
|
// if (path.PathEdges.Count() > 0) {
|
|
// int count = path.PathEdges.Count();
|
|
//
|
|
// double deltaW = count > 1 ? (endWidth - startWidth)/(count - 1) : 1;
|
|
// //if count ==1 the value of deltaW does not matter
|
|
// int i = 0;
|
|
// foreach (var e in path.PathEdges)
|
|
// yield return
|
|
// new DebugCurve(150, startWidth + deltaW*(i++), color, new LineSegment(e.Source, e.Target));
|
|
// }else {
|
|
// int count = path.PathPoints.Count();
|
|
// var pts = path.PathPoints.ToArray();
|
|
//
|
|
// var deltaW = count > 1 ? (endWidth - startWidth) / (count - 1) : 1;
|
|
// //if count ==1 the value of deltaW does not matter
|
|
// for (int i = 0; i < count - 1;i++ )
|
|
// yield return new DebugCurve(150, startWidth+deltaW*i, color, new LineSegment(pts[i], pts[i+1]));
|
|
// }
|
|
// }
|
|
//
|
|
// internal static IEnumerable<DebugCurve> GetTestEdgePathAsDebugCurves(double startWidth, double endWidth, string color, Path path) {
|
|
// int count = path.PathPoints.Count();
|
|
//
|
|
// double deltaW = count > 1 ? (endWidth - startWidth) / (count - 1) : 1; //if count ==1 the value of deltaW does not matter
|
|
// var points = path.PathPoints.ToArray();
|
|
// for (int i = 0; i < points.Length - 1; i++)
|
|
// yield return new DebugCurve(125, startWidth + deltaW * i, color, new LineSegment(points[i], points[i + 1]));
|
|
// }
|
|
//
|
|
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
// static IEnumerable<DebugCurve> GetEdgePathFromPathEdgesAsDebugCurves(double startWidth, double endWidth, string color, Path path) {
|
|
// var points = path.PathPoints.ToArray();
|
|
//
|
|
// int count = points.Length;
|
|
//
|
|
// double deltaW = count > 1 ? (endWidth - startWidth) / (count - 1) : 1; //if count ==1 the value of deltaW does not matter
|
|
// for (int i = 0; i < points.Length - 1; i++)
|
|
// yield return new DebugCurve(120, startWidth + deltaW * i, color, new LineSegment(points[i], points[i + 1]));
|
|
// }
|
|
// // ReSharper disable UnusedMember.Local
|
|
//
|
|
// static internal void ShowEdgePaths(IEnumerable<Polyline> obstacles, IEnumerable<Path> edgePaths) {
|
|
// // ReSharper restore UnusedMember.Local
|
|
// List<DebugCurve> debCurves = GetDebCurvesOfPaths(obstacles, edgePaths);
|
|
//
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(debCurves);
|
|
// }
|
|
//
|
|
// internal static List<DebugCurve> GetDebCurvesOfPaths(IEnumerable<Polyline> enumerable, IEnumerable<Path> edgePaths) {
|
|
// var debCurves = GetObstacleBoundaries(enumerable, "black");
|
|
// int i = 0;
|
|
//
|
|
// foreach (var edgePath in edgePaths)
|
|
// debCurves.AddRange(GetTestEdgePathAsDebugCurves(0.2, 4, DebugCurve.Colors[(i++) % DebugCurve.Colors.Length], edgePath));
|
|
// return debCurves;
|
|
// }
|
|
//
|
|
// internal static void ShowPathsInLoop(IEnumerable<Polyline> enumerable, IEnumerable<Path> edgePaths, Point point) {
|
|
// foreach (var edgePath in edgePaths.Where(path=>(path.PathPoints.First()-point).Length<1 ||(path.PathPoints.Last()-point).Length<1 )) {
|
|
// var debCurves = GetObstacleBoundaries(enumerable, "black");
|
|
// debCurves.AddRange(GetTestEdgePathAsDebugCurves(0.1, 4, "red", edgePath));
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(debCurves);
|
|
// }
|
|
// }
|
|
//
|
|
//
|
|
// // ReSharper disable UnusedMember.Local
|
|
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
// void ShowLongSegsWithIdealPositions(Directions dir) {
|
|
// // ReSharper restore UnusedMember.Local
|
|
// var debCurves = GetObstacleBoundaries(Obstacles, "black");
|
|
// int i = 0;
|
|
//
|
|
// debCurves.AddRange(LongestNudgedSegs.Select(ls => DebugCurveOfLongSeg(ls, DebugCurve.Colors[i++ % DebugCurve.Colors.Length], dir)));
|
|
//
|
|
// DebugCurveCollection.WriteToFile(debCurves, "c:/tmp/longSegs");
|
|
//
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(debCurves);
|
|
// }
|
|
//
|
|
// static DebugCurve DebugCurveOfLongSeg(LongestNudgedSegment ls, string s, Directions dir) {
|
|
// return new DebugCurve(1, s, LineSegOfLongestSeg(ls, dir));
|
|
// }
|
|
//
|
|
// static ICurve LineSegOfLongestSeg(LongestNudgedSegment ls, Directions dir) {
|
|
// var projectionToDir = dir == Directions.East ? (PointProjection)(p => p.X) : (p => p.Y);
|
|
// var min = Double.PositiveInfinity;
|
|
// var max = Double.NegativeInfinity;
|
|
// foreach (var edge in ls.Edges) {
|
|
// UpdateMinMaxWithPoint(ref min, ref max, projectionToDir, edge.Source);
|
|
// UpdateMinMaxWithPoint(ref min, ref max, projectionToDir, edge.Target);
|
|
// }
|
|
// return dir == Directions.East
|
|
// ? new LineSegment(min, -ls.IdealPosition, max, -ls.IdealPosition)
|
|
// : new LineSegment(ls.IdealPosition, min, ls.IdealPosition, max);
|
|
// }
|
|
//
|
|
// static void UpdateMinMaxWithPoint(ref double min, ref double max, PointProjection projectionToDir, Point point) {
|
|
// double p = projectionToDir(point);
|
|
// if (min > p)
|
|
// min = p;
|
|
// if (max < p)
|
|
// max = p;
|
|
// }
|
|
//
|
|
//
|
|
// // ReSharper disable UnusedMember.Local
|
|
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
// void ShowPathsDebug(IEnumerable<Path> edgePaths) {
|
|
// var debCurves = GetObstacleBoundaries(Obstacles, "black");
|
|
// int i = 0;
|
|
//
|
|
// foreach (var edgePath in edgePaths)
|
|
// debCurves.AddRange(GetEdgePathFromPathEdgesAsDebugCurves(0.01, 0.4, DebugCurve.Colors[(i++) % DebugCurve.Colors.Length], edgePath));
|
|
//
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(debCurves);
|
|
// }
|
|
//
|
|
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
// static IEnumerable<DebugCurve> PathDebugCurves(Path path, string color) {
|
|
// var d = path.PathEdges.Select(e => new DebugCurve(70, 0.5, color, new LineSegment(e.Source, e.Target)));
|
|
// return d.Concat(MarkPathVerts(path));
|
|
// }
|
|
//
|
|
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
// private static IEnumerable<DebugCurve> MarkPathVerts(Path path) {
|
|
// bool first = true;
|
|
// var p = new Point();
|
|
// foreach (var p0 in path.PathPoints) {
|
|
// if (first) {
|
|
// yield return new DebugCurve(200, 1, "violet", CurveFactory.CreateDiamond(5, 5, p0));
|
|
// first = false;
|
|
// } else
|
|
// yield return new DebugCurve(100, 0.5, "brown", CurveFactory.CreateEllipse(1.5, 1.5, p0));
|
|
// p = p0;
|
|
// }
|
|
// yield return new DebugCurve(200, 1, "green", CurveFactory.CreateDiamond(3, 3, p));
|
|
// }
|
|
//
|
|
// static internal IEnumerable<DebugCurve> PathDebugCurvesFromPoint(Path path) {
|
|
// var l = new List<Point>(path.PathPoints);
|
|
// for (int i = 0; i < l.Count - 1; i++)
|
|
// yield return new DebugCurve(4, "red", new LineSegment(l[i], l[i + 1]));
|
|
// }
|
|
// //
|
|
// // ReSharper disable UnusedMember.Local
|
|
// // void ShowEdgesOfEdgePath(Path path){
|
|
// // ReSharper restore UnusedMember.Local
|
|
// // string[] colors = {"red", "brown", "purple"};
|
|
// // const double w0 = 1;
|
|
// // const double w1 = 3;
|
|
// // double dw = (w1 - w0)/path.OrientedSubpaths.Count;
|
|
// // int i = 0;
|
|
// // var dc = new List<DebugCurve>();
|
|
// // foreach (var s in path.OrientedSubpaths){
|
|
// // dc.AddRange(SubpathDebugCurves(w0 + dw*i, colors[Math.Min(i++, colors.Length - 1)], s));
|
|
// // }
|
|
// // LayoutAlgorithmSettings.ShowDebugCurves(dc.ToArray());
|
|
// // }
|
|
// //
|
|
// // static IEnumerable<DebugCurve> SubpathDebugCurves(double w, string color, OrientedSubpath subpath){
|
|
// // return subpath.LinkedPath.Select(e => new DebugCurve(w, color, new LineSegment(e.Source.Point, e.Target.Point)));
|
|
// // }
|
|
//
|
|
//
|
|
// static internal List<DebugCurve> GetObstacleBoundaries(IEnumerable<Polyline> obstacles, string color) {
|
|
// var debugCurves = new List<DebugCurve>();
|
|
// if (obstacles != null)
|
|
// debugCurves.AddRange(obstacles.Select(poly => new DebugCurve(50, 0.3, color, poly)));
|
|
// return debugCurves;
|
|
// }
|
|
// #endif
|
|
#endregion
|
|
|
|
void CreateConstraintsBetweenLongestSegments() {
|
|
foreach (var segment in LongestNudgedSegs)
|
|
CreateConstraintsBetweenLongestSegmentsForSegment(segment);
|
|
}
|
|
|
|
void CreateConstraintsBetweenLongestSegmentsForSegment(LongestNudgedSegment segment) {
|
|
var rightNeighbors = new Set<LongestNudgedSegment>();
|
|
foreach (var pathEdge in segment.Edges) {
|
|
var axisEdge = pathEdge.AxisEdge;
|
|
if (axisEdge != null)
|
|
foreach (var rightNeiAxisEdge in axisEdge.RightNeighbors)
|
|
foreach (var longSeg in rightNeiAxisEdge.LongestNudgedSegments)
|
|
rightNeighbors.Insert(longSeg);
|
|
}
|
|
|
|
foreach (var seg in rightNeighbors)
|
|
ConstraintTwoLongestSegs(segment, seg);
|
|
|
|
}
|
|
|
|
void CreateConstraintsOfTheOrder() {
|
|
foreach (var kv in PathOrders)
|
|
if (ParallelToDirection(kv.Key, NudgingDirection))
|
|
CreateConstraintsOfThePathOrder(kv.Value);
|
|
}
|
|
|
|
static bool ParallelToDirection(VisibilityEdge edge, Directions direction) {
|
|
switch (direction) {
|
|
case Directions.North:
|
|
case Directions.South:
|
|
return ApproximateComparer.Close(edge.SourcePoint.X, edge.TargetPoint.X);
|
|
default:
|
|
return ApproximateComparer.Close(edge.SourcePoint.Y, edge.TargetPoint.Y);
|
|
}
|
|
}
|
|
|
|
void CreateConstraintsOfThePathOrder(IEnumerable<PathEdge> pathOrder) {
|
|
PathEdge prevEdge = null;
|
|
|
|
foreach (var pathEdge in pathOrder.Where(p => p.LongestNudgedSegment != null)) {
|
|
if (prevEdge != null)
|
|
ConstraintTwoLongestSegs(prevEdge.LongestNudgedSegment, pathEdge.LongestNudgedSegment);
|
|
prevEdge = pathEdge;
|
|
}
|
|
}
|
|
|
|
void ConstraintTwoLongestSegs(LongestNudgedSegment prevSeg, LongestNudgedSegment seg) {
|
|
if (!prevSeg.IsFixed || !seg.IsFixed)
|
|
Solver.AddConstraint(prevSeg.Id, seg.Id);
|
|
}
|
|
|
|
UniformOneDimensionalSolver Solver { get; set; }
|
|
|
|
void CreateVariablesOfLongestSegment(LongestNudgedSegment segment) {
|
|
if (!segment.IsFixed) {
|
|
var leftBound = segment.GetLeftBound();
|
|
var rightBound = segment.GetRightBound();
|
|
if (leftBound >= rightBound) {//don't move the segment from the way it was generated
|
|
Solver.AddFixedVariable(segment.Id, SegmentPosition(segment, NudgingDirection));
|
|
segment.IsFixed = true;
|
|
} else {
|
|
Solver.AddVariable(segment.Id, SegmentPosition(segment, NudgingDirection), segment.IdealPosition, segment.Width);
|
|
// Debug.Assert(leftBound + Curve.DistanceEpsilon < rightBound); //this assert does not hold for overlaps
|
|
if (leftBound != Double.NegativeInfinity)
|
|
Solver.SetLowBound(leftBound, segment.Id);
|
|
if (rightBound != Double.PositiveInfinity)
|
|
Solver.SetUpperBound(segment.Id, rightBound);
|
|
}
|
|
} else
|
|
Solver.AddFixedVariable(segment.Id, SegmentPosition(segment, NudgingDirection));
|
|
}
|
|
|
|
static double SegmentPosition(SegmentBase segment, Directions direction) {
|
|
return direction == Directions.North ? segment.Start.X : -segment.Start.Y;
|
|
}
|
|
|
|
// #if TEST_MSAGL
|
|
// // ReSharper disable UnusedMember.Local
|
|
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
// void ShowSegmentBounds(LongestNudgedSegment segment) {
|
|
// // ReSharper restore UnusedMember.Local
|
|
// var dd = GetObstacleBoundaries(Obstacles, "black");
|
|
// var segtop = segment.Edges.Max(e => Math.Max(e.Source.Y, e.Target.Y));
|
|
// var segbottom = segment.Edges.Min(e => Math.Min(e.Source.Y, e.Target.Y));
|
|
// var segx = segment.Start.X;
|
|
// var seg = new DebugCurve(80, 1, "brown", new LineSegment(new Point(segx, segbottom), new Point(segx, segtop)));
|
|
//
|
|
// var leftbound = new DebugCurve(80, 1.0, "red", new LineSegment(new Point(segment.GetLeftBound(), segbottom),
|
|
// new Point(segment.GetLeftBound(), segtop)));
|
|
// var rightbound = new DebugCurve(80, 1, "green", new LineSegment(new Point(segment.GetRightBound(), segbottom),
|
|
// new Point(segment.GetRightBound(), segtop)));
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(dd.Concat(new[] { seg, leftbound, rightbound }));
|
|
// }
|
|
// // ReSharper disable UnusedMember.Local
|
|
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
// void ShowSegment(LongestNudgedSegment segment) {
|
|
// // ReSharper restore UnusedMember.Local
|
|
// var dd = GetObstacleBoundaries(Obstacles, "black");
|
|
// var segtop = segment.Edges.Max(e => Math.Max(e.Source.Y, e.Target.Y));
|
|
// var segbottom = segment.Edges.Min(e => Math.Min(e.Source.Y, e.Target.Y));
|
|
// var segx = segment.Start.X;
|
|
// var seg = new DebugCurve(80, 1, "brown", new LineSegment(new Point(segx, segbottom), new Point(segx, segtop)));
|
|
//
|
|
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(dd.Concat(new[] { seg }));
|
|
//
|
|
// }
|
|
// #endif
|
|
|
|
List<LongestNudgedSegment> LongestNudgedSegs { get; set; }
|
|
|
|
Dictionary<AxisEdge, List<PathEdge>> PathOrders { get; set; }
|
|
/// <summary>
|
|
/// maps each path to the pair of obstacles; the first element of the pair is
|
|
/// where the path starts and the second where the path ends
|
|
/// </summary>
|
|
Dictionary<Path, Tuple<Polyline, Polyline>> PathToObstacles { get; set; }
|
|
|
|
|
|
void FindFreeSpaceInDirection(IEnumerable<AxisEdge> axisEdges) {
|
|
BoundAxisEdgesByRectsKnownInAdvance();
|
|
var freeSpaceFinder = new FreeSpaceFinder(NudgingDirection, Obstacles,
|
|
axisEdgesToObstaclesTheyOriginatedFrom, PathOrders, axisEdges);
|
|
freeSpaceFinder.FindFreeSpace();
|
|
}
|
|
|
|
void BoundAxisEdgesByRectsKnownInAdvance() {
|
|
foreach (var path in Paths) {
|
|
if (HasGroups)
|
|
BoundPathByMinCommonAncestors(path);
|
|
BoundAxisEdgesAdjacentToSourceAndTargetOnEdge(path);
|
|
}
|
|
}
|
|
|
|
void BoundPathByMinCommonAncestors(Path path) {
|
|
foreach (var rect in GetMinCommonAncestors(path.EdgeGeometry).Select(sh => sh.BoundingBox))
|
|
foreach (
|
|
var edge in
|
|
path.PathEdges.Select(e => e.AxisEdge).Where(
|
|
axisEdge => axisEdge.Direction == NudgingDirection)
|
|
)
|
|
BoundAxisEdgeByRect(rect, edge);
|
|
}
|
|
|
|
|
|
IEnumerable<Shape> GetMinCommonAncestors(EdgeGeometry edgeGeometry) {
|
|
if (PortToShapes == null)
|
|
PortToShapes = MapPortsToShapes(AncestorsSets.Keys);
|
|
var commonAncestors = AncestorsForPort(edgeGeometry.SourcePort)*
|
|
AncestorsForPort(edgeGeometry.TargetPort);
|
|
return commonAncestors.Where(anc => !anc.Children.Any(child=>commonAncestors.Contains(child)));
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
protected Dictionary<Port, Shape> PortToShapes { get; private set; }
|
|
|
|
Set<Shape> AncestorsForPort(Port port) {
|
|
Shape shape;
|
|
if (PortToShapes.TryGetValue(port, out shape)) {
|
|
return AncestorsSets[shape];
|
|
}
|
|
|
|
// This is a FreePort or Waypoint; return all spatial parents.
|
|
return new Set<Shape>(HierarchyOfGroups.AllHitItems(new Rectangle(port.Location, port.Location), null));
|
|
}
|
|
|
|
|
|
void BoundAxisEdgeAdjacentToObstaclePort(Port port, AxisEdge axisEdge) {
|
|
if (port is WaypointPort || (port.Curve == null && port.PortEntry == null))
|
|
BoundAxisByPoint(port.Location, axisEdge);
|
|
else if (port.PortEntry == null) {
|
|
if (port.Curve.BoundingBox.Contains(port.Location))
|
|
BoundAxisEdgeByRect(port.Curve.BoundingBox, axisEdge);
|
|
} else {
|
|
var portEntry = port.PortEntry as PortEntryOnCurve;
|
|
if (portEntry != null) {
|
|
Rectangle rect;
|
|
if (FindPortEntryRectCrossingAxisEdge(portEntry, axisEdge, out rect))
|
|
BoundAxisEdgeByRect(rect, axisEdge);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BoundAxisByPoint(Point point, AxisEdge axisEdge) {
|
|
if (axisEdge != null && axisEdge.Direction == NudgingDirection)
|
|
if (NudgingDirection == Directions.North) {
|
|
axisEdge.BoundFromLeft(point.X);
|
|
axisEdge.BoundFromRight(point.X);
|
|
} else {
|
|
axisEdge.BoundFromLeft(-point.Y);
|
|
axisEdge.BoundFromRight(-point.Y);
|
|
}
|
|
}
|
|
|
|
static bool FindPortEntryRectCrossingAxisEdge(PortEntryOnCurve portEntry, AxisEdge axisEdge, out Rectangle rect) {
|
|
var ar = new Rectangle(axisEdge.SourcePoint, axisEdge.TargetPoint);
|
|
foreach (Rectangle r in portEntry.AllowedRectangles) {
|
|
if(r.Intersects(ar)) {
|
|
rect = r;
|
|
return true;
|
|
}
|
|
}
|
|
rect = Rectangle.CreateAnEmptyBox();
|
|
return false;
|
|
}
|
|
|
|
void BoundAxisEdgesAdjacentToSourceAndTargetOnEdge(Path path) {
|
|
BoundAxisEdgeAdjacentToObstaclePort(path.EdgeGeometry.SourcePort, path.FirstEdge.AxisEdge);
|
|
BoundAxisEdgeAdjacentToObstaclePort(path.EdgeGeometry.TargetPort, path.LastEdge.AxisEdge);
|
|
}
|
|
|
|
void BoundAxisEdgeByRect(Rectangle rectangle, AxisEdge axisEdge) {
|
|
if (axisEdge != null && axisEdge.Direction == NudgingDirection)
|
|
if (NudgingDirection == Directions.North) {
|
|
axisEdge.BoundFromLeft(rectangle.Left);
|
|
axisEdge.BoundFromRight(rectangle.Right);
|
|
} else {
|
|
axisEdge.BoundFromLeft(-rectangle.Top);
|
|
axisEdge.BoundFromRight(-rectangle.Bottom);
|
|
}
|
|
}
|
|
|
|
|
|
void CreateLongestNudgedSegments() {
|
|
var projectionToPerp =
|
|
NudgingDirection == Directions.East ? (PointProjection)FreeSpaceFinder.MinusY : FreeSpaceFinder.X;
|
|
|
|
LongestNudgedSegs = new List<LongestNudgedSegment>();
|
|
foreach (var path in Paths)
|
|
CreateLongestNudgedSegmentsForPath(path, projectionToPerp);
|
|
|
|
}
|
|
|
|
|
|
void CreateLongestNudgedSegmentsForPath(Path path, PointProjection projectionToPerp) {
|
|
//ShowEdgesOfEdgePath(path);
|
|
GoOverPathAndCreateLongSegs(path);
|
|
CalculateIdealPositionsForLongestSegs(path, projectionToPerp);
|
|
}
|
|
|
|
static void CalculateIdealPositionsForLongestSegs(Path path, PointProjection projectionToPerp) {
|
|
LongestNudgedSegment currentLongSeg = null;
|
|
LongestNudgedSegment ret = null;
|
|
double prevOffset = projectionToPerp(path.Start);
|
|
foreach (var edge in path.PathEdges) {
|
|
if (edge.LongestNudgedSegment != null) {
|
|
currentLongSeg = edge.LongestNudgedSegment;
|
|
if (ret != null) {
|
|
double t;
|
|
SetIdealPositionForSeg(ret, t = projectionToPerp(ret.Start), prevOffset,
|
|
projectionToPerp(currentLongSeg.Start));
|
|
prevOffset = t;
|
|
ret = null;
|
|
}
|
|
} else if (currentLongSeg != null) {
|
|
ret = currentLongSeg;
|
|
currentLongSeg = null;
|
|
}
|
|
}
|
|
if (ret != null)
|
|
SetIdealPositionForSeg(ret, projectionToPerp(ret.Start), prevOffset, projectionToPerp(path.End));
|
|
else if (currentLongSeg != null)
|
|
currentLongSeg.IdealPosition = projectionToPerp(currentLongSeg.Start);
|
|
}
|
|
|
|
static void SetIdealPositionForSeg(LongestNudgedSegment segment, double segPosition, double offset0, double offset1) {
|
|
var max = Math.Max(offset0, offset1);
|
|
var min = Math.Min(offset0, offset1);
|
|
if (min + ApproximateComparer.DistanceEpsilon < segPosition)
|
|
if (segPosition < max)
|
|
segment.IdealPosition = 0.5 * (max + min);
|
|
else
|
|
segment.IdealPosition = max;
|
|
else
|
|
segment.IdealPosition = min;
|
|
|
|
|
|
}
|
|
|
|
void GoOverPathAndCreateLongSegs(Path path) {
|
|
LongestNudgedSegment currentLongestSeg = null;
|
|
|
|
var oppositeDir = CompassVector.OppositeDir(NudgingDirection);
|
|
|
|
foreach (var edge in path.PathEdges) {
|
|
var edgeDir = edge.Direction;
|
|
if (edgeDir == NudgingDirection || edgeDir == oppositeDir) {
|
|
if (currentLongestSeg == null)
|
|
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=368
|
|
{
|
|
edge.LongestNudgedSegment = currentLongestSeg = new LongestNudgedSegment(LongestNudgedSegs.Count);
|
|
LongestNudgedSegs.Add(edge.LongestNudgedSegment);
|
|
}
|
|
#else
|
|
LongestNudgedSegs.Add(
|
|
edge.LongestNudgedSegment =
|
|
currentLongestSeg = new LongestNudgedSegment(LongestNudgedSegs.Count));
|
|
#endif
|
|
else
|
|
edge.LongestNudgedSegment = currentLongestSeg;
|
|
|
|
if (edge.IsFixed)
|
|
currentLongestSeg.IsFixed = true;
|
|
} else {
|
|
//the edge is perpendicular to "direction"
|
|
edge.LongestNudgedSegment = null;
|
|
currentLongestSeg = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
static IEnumerable<Point> BuildPolylineForPath(Path path) {
|
|
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369
|
|
var points = path.PathPoints.Select(p=>p.Clone()).ToArray();
|
|
#else
|
|
var points = path.PathPoints.ToArray();
|
|
#endif
|
|
ExtendPolylineToPorts(ref points, path);
|
|
for(int i=0;i<points.Length-1;i++)
|
|
Debug.Assert(CompassVector.IsPureDirection(points[i], points[i+1]));
|
|
|
|
return points;
|
|
}
|
|
|
|
static void ExtendPolylineToPorts(ref Point[] points, Path path) {
|
|
ExtendPolylineToSourcePort(ref points, path.EdgeGeometry.SourcePort.Location);
|
|
ExtendPolylineToTargetPort(ref points, path.EdgeGeometry.TargetPort.Location);
|
|
|
|
// In some overlapped cases where the source or target vertex used for the path
|
|
// coincides with the target or source port location, we can end up with a single-point
|
|
// path. In that case, we just force a straightline path.
|
|
if (points.Length < 2) {
|
|
points = new Point[2];
|
|
points[0] = path.EdgeGeometry.SourcePort.Location;
|
|
points[1] = path.EdgeGeometry.TargetPort.Location;
|
|
}
|
|
}
|
|
|
|
static void ExtendPolylineToTargetPort(ref Point[] points, Point location) {
|
|
int n = points.Length - 1;
|
|
var dir = CompassVector.VectorDirection(points[n - 1], points[n]);
|
|
if (ProjectionsAreClose(points[n-1], dir, location)) {
|
|
//it might be that the last point on polyline is at the port already
|
|
//then we just drop the last point
|
|
points = points.Take(n).ToArray();
|
|
return;
|
|
}
|
|
if (dir == Directions.East || dir == Directions.West)
|
|
points[n].X = location.X;
|
|
else
|
|
points[n].Y = location.Y;
|
|
}
|
|
|
|
static bool ProjectionsAreClose(Point a, Directions dir, Point b) {
|
|
if (dir == Directions.East || dir == Directions.West)
|
|
return ApproximateComparer.Close(a.X, b.X);
|
|
return ApproximateComparer.Close(a.Y, b.Y);
|
|
}
|
|
|
|
static void ExtendPolylineToSourcePort(ref Point[] points, Point location) {
|
|
var dir = CompassVector.VectorDirection(points[0], points[1]);
|
|
if (ProjectionsAreClose(points[1], dir, location)) {
|
|
//it might be that the second point on polyline is at the port already
|
|
//then we just drop the first point
|
|
points = points.Skip(1).ToArray();
|
|
return;
|
|
}
|
|
if (dir == Directions.East || dir == Directions.West)
|
|
points[0].X = location.X;
|
|
else
|
|
points[0].Y = location.Y;
|
|
}
|
|
|
|
static IEnumerable<Point> RemoveSwitchbacksAndMiddlePoints(IEnumerable<Point> points) {
|
|
var en = points.GetEnumerator();
|
|
en.MoveNext();
|
|
var a = en.Current;
|
|
yield return a;
|
|
en.MoveNext();
|
|
var b = en.Current;
|
|
var prevDir = (b - a).CompassDirection;
|
|
|
|
while (en.MoveNext()) {
|
|
var dir = (en.Current - b).CompassDirection;
|
|
if (dir == prevDir || CompassVector.OppositeDir(dir) == prevDir || dir == Directions.None) //we continue walking along the same straight line, maybe going backwards!
|
|
b = en.Current;
|
|
else {
|
|
if (!ApproximateComparer.Close(a, b)) {//make sure that we are not returning the same point twice
|
|
yield return a=Rectilinearise(a, b);
|
|
}
|
|
b = en.Current;
|
|
prevDir = dir;
|
|
}
|
|
}
|
|
if (!ApproximateComparer.Close(a, b))
|
|
yield return Rectilinearise(a,b);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// this function defines the final path coordinates
|
|
/// </summary>
|
|
/// <param name="paths">the set of paths, point sequences</param>
|
|
/// <param name="cornerFitRadius">the radius of the arc inscribed into the path corners</param>
|
|
/// <param name="paddedObstacles">an enumeration of padded obstacles</param>
|
|
/// <param name="ancestorsSets"></param>
|
|
/// <param name="removeStaircases"></param>
|
|
/// <returns>the mapping of the path to its modified path</returns>
|
|
internal static void NudgePaths(IEnumerable<Path> paths, double cornerFitRadius, IEnumerable<Polyline> paddedObstacles, Dictionary<Shape, Set<Shape>> ancestorsSets, bool removeStaircases) {
|
|
if (!paths.Any())
|
|
return;
|
|
var nudger = new Nudger(paths, cornerFitRadius, paddedObstacles, ancestorsSets);
|
|
nudger.Calculate(Directions.North, true);
|
|
nudger.Calculate(Directions.East, false);
|
|
nudger.Calculate(Directions.North, false);
|
|
if (removeStaircases)
|
|
nudger.RemoveStaircases();
|
|
foreach (var path in paths)
|
|
path.EdgeGeometry.Curve = new Polyline(BuildPolylineForPath(path));
|
|
}
|
|
|
|
void RemoveStaircases() {
|
|
StaircaseRemover.RemoveStaircases(Paths, HierarchyOfObstacles);
|
|
|
|
}
|
|
/*
|
|
void RemoveStaircasesFromPath(Path path) {
|
|
|
|
var points = (Point[]) path.PathPoints;
|
|
if (points.Length <= 4) return;
|
|
int i = FindStaircase(points);
|
|
if (i == -1) return;
|
|
var linkedList = new LinkedList<Point>(points);
|
|
var node = linkedList.First;
|
|
for(int j=0;j<i;j++)
|
|
node = node.Next; //getting to the i-th node
|
|
|
|
RemoveStaircaseOnNode(node);
|
|
node = node.Next;
|
|
while(true) {
|
|
node = FindStaircaseAfterNode(node);
|
|
if (node == null) break;
|
|
RemoveStaircaseOnNode(node);
|
|
node = node.Next;
|
|
}
|
|
}
|
|
|
|
LinkedListNode<Point> FindStaircaseAfterNode(LinkedListNode<Point> n) {
|
|
do {
|
|
var nn = n.Next;
|
|
var nnn = nn.Next;
|
|
var nnnn = nnn.Next;
|
|
if (nnn.Next == null)
|
|
return null;
|
|
if (IsStaircase(n.Value, nn.Value, nnn.Value, nnnn.Value))
|
|
return n;
|
|
n = nn;
|
|
} while (true);
|
|
}
|
|
|
|
bool IsStaircase(Point a, Point b, Point c, Point d) {
|
|
|
|
return false;
|
|
}
|
|
|
|
void RemoveStaircaseOnNode(LinkedListNode<Point> node) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
int FindStaircase(Point[] points) {
|
|
throw new NotImplementedException();
|
|
}
|
|
*/
|
|
|
|
internal static Dictionary<Port, Shape> MapPortsToShapes(IEnumerable<Shape> listOfShapes) {
|
|
var portToShapes = new Dictionary<Port, Shape>();
|
|
foreach (Shape shape in listOfShapes)
|
|
foreach (Port port in shape.Ports)
|
|
portToShapes[port] = shape;
|
|
return portToShapes;
|
|
}
|
|
}
|
|
}
|