using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Msagl.Core.DataStructures;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Geometry.Curves;
#if DEBUG
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.DebugHelpers;
#endif
using Microsoft.Msagl.Routing.Visibility;
namespace Microsoft.Msagl.Routing.Spline.ConeSpanner {
///
/// sweeps a given direction of cones and adds discovered edges to the graph
///
internal class LineSweeper : LineSweeperBase, IConeSweeper {
public Point ConeRightSideDirection { get; set; }
public Point ConeLeftSideDirection { get; set; }
readonly ConeSideComparer coneSideComparer;
readonly VisibilityGraph visibilityGraph;
readonly RbTree rightConeSides;
readonly RbTree leftConeSides;
VisibilityGraph portEdgesGraph;
internal Func PortEdgesCreator { get; set; }
LineSweeper(IEnumerable obstacles, Point direction, Point coneRsDir, Point coneLsDir,
VisibilityGraph visibilityGraph, Set ports, Polyline borderPolyline)
: base(obstacles, direction) {
this.visibilityGraph = visibilityGraph;
ConeRightSideDirection = coneRsDir;
ConeLeftSideDirection = coneLsDir;
coneSideComparer = new ConeSideComparer(this);
leftConeSides = new RbTree(coneSideComparer);
rightConeSides = new RbTree(coneSideComparer);
Ports = ports;
BorderPolyline = borderPolyline;
PortEdgesCreator = (a, b) => new TollFreeVisibilityEdge(a, b);
}
Polyline BorderPolyline { get; set; }
internal static void Sweep(
IEnumerable obstacles,
Point direction,
double coneAngle,
VisibilityGraph visibilityGraph,
Set ports,
Polyline borderPolyline) {
var cs = new LineSweeper(obstacles, direction, direction.Rotate(-coneAngle/2),
direction.Rotate(coneAngle/2), visibilityGraph, ports,
borderPolyline);
cs.Calculate();
}
void Calculate() {
InitQueueOfEvents();
while (EventQueue.Count > 0)
ProcessEvent(EventQueue.Dequeue());
if (BorderPolyline != null)
CloseRemainingCones();
CreatePortEdges();
}
void CreatePortEdges() {
if (portEdgesGraph != null)
foreach (var edge in portEdgesGraph.Edges) {
visibilityGraph.AddEdge(edge.SourcePoint, edge.TargetPoint, PortEdgesCreator);
}
}
void CloseRemainingCones() {
if (leftConeSides.Count == 0)
return;
Debug.Assert(leftConeSides.Count == rightConeSides.Count);
PolylinePoint p = BorderPolyline.StartPoint;
var steps=leftConeSides.Count; //we cannot make more than leftConeSides.Count if the data is correct
//because at each step we remove at least one cone
do {
var cone = leftConeSides.TreeMinimum().Item.Cone;
p = FindPolylineSideIntersectingConeRightSide(p, cone);
p = GetPolylinePointInsideOfConeAndRemoveCones(p, cone);
steps--;
} while (leftConeSides.Count > 0 && steps>0);
}
PolylinePoint GetPolylinePointInsideOfConeAndRemoveCones(PolylinePoint p, Cone cone) {
var pn = p.NextOnPolyline;
Point insidePoint = FindInsidePoint(p.Point, pn.Point, cone);
if (ApproximateComparer.Close(insidePoint, p.Point)) {
AddEdgeAndRemoveCone(cone, p.Point);
AddEdgesAndRemoveRemainingConesByPoint(p.Point);
//we don't move p forward here. In the next iteration we just cross [p,pn] with the new leftmost cone right side
} else if (ApproximateComparer.Close(insidePoint, pn.Point)) {
AddEdgeAndRemoveCone(cone, pn.Point);
AddEdgesAndRemoveRemainingConesByPoint(pn.Point);
p = pn;
} else {
p = InsertPointIntoPolylineAfter(BorderPolyline, p, insidePoint);
AddEdgeAndRemoveCone(cone, p.Point);
AddEdgesAndRemoveRemainingConesByPoint(p.Point);
}
return p;
}
static Point FindInsidePoint(Point leftPoint, Point rightPoint, Cone cone) {
// if (debug)
// LayoutAlgorithmSettings.Show(CurveFactory.CreateCircle(3, leftPoint),
// CurveFactory.CreateDiamond(3, 3, rightPoint),
// BorderPolyline, ExtendSegmentToZ(cone.LeftSide),
// ExtendSegmentToZ(cone.RightSide));
return FindInsidePointBool(leftPoint, rightPoint, cone.Apex, cone.Apex + cone.LeftSideDirection,
cone.Apex + cone.RightSideDirection);
}
static Point FindInsidePointBool(Point leftPoint, Point rightPoint, Point apex, Point leftSideConePoint,
Point rightSideConePoint) {
if (ApproximateComparer.Close(leftPoint, rightPoint))
return leftPoint; //does not matter which one to return
if (Point.PointIsInsideCone(leftPoint, apex, leftSideConePoint, rightSideConePoint))
return leftPoint;
if (Point.PointIsInsideCone(rightPoint, apex, leftSideConePoint, rightSideConePoint))
return rightPoint;
var m = 0.5 * (leftPoint + rightPoint);
if (Point.PointToTheLeftOfLine(m, apex, leftSideConePoint))
return FindInsidePointBool(m, rightPoint, apex, leftSideConePoint, rightSideConePoint);
return FindInsidePointBool(leftPoint, m, apex, leftSideConePoint, rightSideConePoint);
}
void AddEdgesAndRemoveRemainingConesByPoint(Point point) {
var conesToRemove = new List();
foreach (var leftConeSide in leftConeSides) {
if (Point.PointToTheRightOfLineOrOnLine(point, leftConeSide.Start,
leftConeSide.Start + leftConeSide.Direction))
conesToRemove.Add(leftConeSide.Cone);
else
break;
}
foreach (var cone in conesToRemove) {
AddEdgeAndRemoveCone(cone, point);
}
}
PolylinePoint FindPolylineSideIntersectingConeRightSide(PolylinePoint p, Cone cone) {
var startPoint = p;
var a = cone.Apex;
var b = cone.Apex + ConeRightSideDirection;
var pSign = GetSign(p, a, b);
do {
var pn = p.NextOnPolyline;
var pnSigh = GetSign(pn, a, b);
if (pnSigh - pSign > 0)
return p;
p = pn;
pSign = pnSigh;
if (p == startPoint)
throw new InvalidOperationException();
} while (true);
/*
throw new InvalidOleVariantTypeException();
*/
}
static int GetSign(PolylinePoint p, Point a, Point b) {
var d = Point.SignedDoubledTriangleArea(a, b, p.Point);
if (d < 0)
return 1;
return d > 0 ? -1 : 0;
}
// #if DEBUG && TEST_MSAGL
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// void Showside(PolylinePoint p, Point a, Point b, PolylinePoint pn) {
// ShowBothTrees(new DebugCurve(100, 1, "brown", BorderPolyline), new DebugCurve(100, 2, "blue",
// new LineSegment(a, b)),
// new DebugCurve(100, 2, "green",
// new LineSegment(
// pn.Point, p.Point)
// ));
// }
// #endif
// void CheckThatPolylineIsLegal()
// {
// var p = BorderPolyline.StartPoint;
// do
// {
// var pn = p.NextOnPolyline;
// Debug.Assert(!ApproximateComparer.Close(p.Point, pn.Point));
// Debug.Assert((pn.Point - p.Point)*(pn.NextOnPolyline.Point - pn.Point) > -ApproximateComparer.Tolerance);
// p = pn;
// } while (p != BorderPolyline.StartPoint);
// }
// #if TEST_MSAGL
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// void ShowBoundaryPolyline() {
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(CreateBoundaryPolyDebugCurves());
// }
//
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// IEnumerable CreateBoundaryPolyDebugCurves() {
// int i = 0;
// for (var p = BorderPolyline.StartPoint; p != null; p = p.Next) {
// yield return new DebugCurve(new Ellipse(1, 1, p.Point), i++);
// }
// }
// #endif
void AddEdgeAndRemoveCone(Cone cone, Point p) {
if (Ports != null && Ports.Contains(cone.Apex))
CreatePortEdge(cone, p);
else
visibilityGraph.AddEdge(cone.Apex, p);
RemoveCone(cone);
}
/*********************
A complication arises when we have overlaps. Loose obstacles become large enough to contain several
ports. We need to avoid a situation when a port has degree more than one.
To avoid this situation we redirect to p every edge incoming into cone.Apex.
Notice that we create a new graph to keep port edges for ever
direction of the sweep and the above procedure just alignes the edges better.
In the resulting graph, which contains the sum of the graphs passed to AddDirection, of course
a port can have an incoming and outcoming edge at the same time
*******************/
void CreatePortEdge(Cone cone, Point p) {
if (portEdgesGraph == null) portEdgesGraph = new VisibilityGraph();
var coneApexVert = portEdgesGraph.FindVertex(cone.Apex);
//all previous edges adjacent to cone.Apex
var edgesToFix = (coneApexVert != null)
? coneApexVert.InEdges.Concat(coneApexVert.OutEdges).ToArray()
: null;
if (edgesToFix != null)
foreach (var edge in edgesToFix) {
var otherPort = (edge.Target == coneApexVert ? edge.Source : edge.Target).Point;
VisibilityGraph.RemoveEdge(edge);
portEdgesGraph.AddEdge(otherPort, p);
}
portEdgesGraph.AddEdge(cone.Apex, p);
}
internal static PolylinePoint InsertPointIntoPolylineAfter(Polyline borderPolyline, PolylinePoint insertAfter,
Point pointToInsert) {
PolylinePoint np;
if (insertAfter.Next != null) {
np = new PolylinePoint(pointToInsert) { Prev = insertAfter, Next = insertAfter.Next, Polyline = borderPolyline };
insertAfter.Next.Prev = np;
insertAfter.Next = np;
} else {
np = new PolylinePoint(pointToInsert) { Prev = insertAfter, Polyline = borderPolyline };
insertAfter.Next = np;
borderPolyline.EndPoint = np;
}
Debug.Assert(
!(ApproximateComparer.Close(np.Point, np.PrevOnPolyline.Point) ||
ApproximateComparer.Close(np.Point, np.NextOnPolyline.Point)));
borderPolyline.RequireInit();
return np;
}
void ProcessEvent(SweepEvent p) {
// if(debug && (p.Site-new Point(-313.122 ,-170.6)).Length<1)
// Show( EllipseOnVert(p));
var vertexEvent = p as VertexEvent;
if (vertexEvent != null)
ProcessVertexEvent(vertexEvent);
else {
var rightIntersectionEvent = p as RightIntersectionEvent;
if (rightIntersectionEvent != null)
ProcessRightIntersectionEvent(rightIntersectionEvent);
else {
var leftIntersectionEvent = p as LeftIntersectionEvent;
if (leftIntersectionEvent != null)
ProcessLeftIntersectionEvent(leftIntersectionEvent);
else {
var coneClosure = p as ConeClosureEvent;
if (coneClosure != null) {
if (!coneClosure.ConeToClose.Removed)
RemoveCone(coneClosure.ConeToClose);
} else
ProcessPortObstacleEvent((PortObstacleEvent)p);
Z = GetZ(p);
}
}
}
//Debug.Assert(TreesAreCorrect());
}
#if TEST_MSAGL
// protected override bool TreesAreCorrect() {
// return TreeIsCorrect(leftConeSides) && TreeIsCorrect(rightConeSides);
// }
//
// bool TreeIsCorrect(RbTree tree) {
// var y = double.NegativeInfinity;
// foreach (var t in tree) {
// var x = coneSideComparer.IntersectionOfSegmentAndSweepLine(t);
// var yp = x*DirectionPerp;
// if (yp < y - ApproximateComparer.DistanceEpsilon)
// return false;
// y = yp;
// }
// return true;
// }
#endif
void ProcessPortObstacleEvent(PortObstacleEvent portObstacleEvent) {
Z = GetZ(portObstacleEvent);
GoOverConesSeeingVertexEvent(portObstacleEvent);
CreateConeOnVertex(portObstacleEvent);
}
void ProcessLeftIntersectionEvent(LeftIntersectionEvent leftIntersectionEvent) {
if (leftIntersectionEvent.coneLeftSide.Removed == false) {
if (Math.Abs((leftIntersectionEvent.EndVertex.Point - leftIntersectionEvent.Site) * SweepDirection) <
ApproximateComparer.DistanceEpsilon) {
//the cone is totally covered by a horizontal segment
RemoveCone(leftIntersectionEvent.coneLeftSide.Cone);
} else {
RemoveSegFromLeftTree(leftIntersectionEvent.coneLeftSide);
Z = SweepDirection * leftIntersectionEvent.Site; //it is safe now to restore the order
var leftSide = new BrokenConeSide(
leftIntersectionEvent.Site,
leftIntersectionEvent.EndVertex, leftIntersectionEvent.coneLeftSide);
InsertToTree(leftConeSides, leftSide);
leftIntersectionEvent.coneLeftSide.Cone.LeftSide = leftSide;
LookForIntersectionOfObstacleSideAndLeftConeSide(leftIntersectionEvent.Site,
leftIntersectionEvent.EndVertex);
TryCreateConeClosureForLeftSide(leftSide);
}
} else
Z = SweepDirection * leftIntersectionEvent.Site;
}
void TryCreateConeClosureForLeftSide(BrokenConeSide leftSide) {
var coneRightSide = leftSide.Cone.RightSide as ConeRightSide;
if (coneRightSide != null)
if (
Point.GetTriangleOrientation(coneRightSide.Start, coneRightSide.Start + coneRightSide.Direction,
leftSide.EndVertex.Point) == TriangleOrientation.Clockwise)
CreateConeClosureEvent(leftSide, coneRightSide);
}
void CreateConeClosureEvent(BrokenConeSide brokenConeSide, ConeSide otherSide) {
Point x;
#if DEBUG && TEST_MSAGL
var r =
#endif
Point.RayIntersectsRayInteriors(brokenConeSide.start, brokenConeSide.Direction, otherSide.Start,
otherSide.Direction, out x);
// #if DEBUG && TEST_MSAGL
// if (!r)
// LayoutAlgorithmSettings.ShowDebugCurves(
// new DebugCurve(100, 0.1, "red",new LineSegment(brokenConeSide.Start, brokenConeSide.start + brokenConeSide.Direction)),
// new DebugCurve(100,0.1, "black", new Ellipse(0.1,0.1, brokenConeSide.Start)),
// new DebugCurve(100, 0.1, "blue",new LineSegment(otherSide.Start, otherSide.Start + otherSide.Direction)));
// Debug.Assert(r);
// #endif
EnqueueEvent(new ConeClosureEvent(x, brokenConeSide.Cone));
}
void ProcessRightIntersectionEvent(RightIntersectionEvent rightIntersectionEvent) {
//restore Z for the time being
// Z = PreviousZ;
if (rightIntersectionEvent.coneRightSide.Removed == false) {
//it can happen that the cone side participating in the intersection is gone;
//obstracted by another obstacle or because of a vertex found inside of the cone
//PrintOutRightSegTree();
RemoveSegFromRightTree(rightIntersectionEvent.coneRightSide);
Z = SweepDirection * rightIntersectionEvent.Site;
var rightSide = new BrokenConeSide(
rightIntersectionEvent.Site,
rightIntersectionEvent.EndVertex, rightIntersectionEvent.coneRightSide);
InsertToTree(rightConeSides, rightSide);
rightIntersectionEvent.coneRightSide.Cone.RightSide = rightSide;
LookForIntersectionOfObstacleSideAndRightConeSide(rightIntersectionEvent.Site,
rightIntersectionEvent.EndVertex);
TryCreateConeClosureForRightSide(rightSide);
} else
Z = SweepDirection * rightIntersectionEvent.Site;
}
void TryCreateConeClosureForRightSide(BrokenConeSide rightSide) {
var coneLeftSide = rightSide.Cone.LeftSide as ConeLeftSide;
if (coneLeftSide != null)
if (
Point.GetTriangleOrientation(coneLeftSide.Start, coneLeftSide.Start + coneLeftSide.Direction,
rightSide.EndVertex.Point) == TriangleOrientation.Counterclockwise)
CreateConeClosureEvent(rightSide, coneLeftSide);
}
void RemoveConesClosedBySegment(Point leftPoint, Point rightPoint) {
CloseConesCoveredBySegment(leftPoint, rightPoint,
SweepDirection * leftPoint > SweepDirection * rightPoint
? leftConeSides
: rightConeSides);
}
void CloseConesCoveredBySegment(Point leftPoint, Point rightPoint, RbTree tree) {
var node = tree.FindFirst(
s => Point.GetTriangleOrientation(s.Start, s.Start + s.Direction, leftPoint) ==
TriangleOrientation.Counterclockwise);
Point x;
if (node == null || !Point.IntervalIntersectsRay(leftPoint, rightPoint,
node.Item.Start, node.Item.Direction, out x))
return;
var conesToRemove = new List();
do {
conesToRemove.Add(node.Item.Cone);
node = tree.Next(node);
} while (node != null && Point.IntervalIntersectsRay(leftPoint, rightPoint,
node.Item.Start, node.Item.Direction, out x));
foreach (var cone in conesToRemove)
RemoveCone(cone);
}
void ProcessVertexEvent(VertexEvent vertexEvent) {
// if (count == 872 && (vertexEvent.Site - new Point(-130.7368, -305.908)).Length < 1)
// Show(EllipseOnVert(vertexEvent));
Z = GetZ(vertexEvent);
//PrintOutLeftSegTree();
//PrintOutRightSegTree();
GoOverConesSeeingVertexEvent(vertexEvent);
AddConeAndEnqueueEvents(vertexEvent);
}
// #if TEST_MSAGL
// // ReSharper disable UnusedMember.Local
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// static Ellipse EllipseOnVert(SweepEvent vertexEvent) {
// // ReSharper restore UnusedMember.Local
// return new Ellipse(5, 5, vertexEvent.Site);
// }
//
// // ReSharper disable UnusedMember.Local
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// static Ellipse EllipseOnPolylinePoint(PolylinePoint pp) {
// // ReSharper restore UnusedMember.Local
// return EllipseOnPolylinePoint(pp, 5);
// }
// // ReSharper disable UnusedMember.Local
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// static Ellipse EllipseOnPolylinePoint(PolylinePoint pp, double i)
// // ReSharper restore UnusedMember.Local
// {
// return new Ellipse(i, i, pp.Point);
// }
//
// static ICurve Diamond(Point p) {
// return CurveFactory.CreateDiamond(2, 2, p);
// }
//
// // ReSharper disable UnusedMember.Local
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",
// "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String)"
// ),
// System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// void CheckConsistency() {
// // ReSharper restore UnusedMember.Local
// foreach (var s in rightConeSides) {
// coneSideComparer. SetOperand(s);
// if (!leftConeSides.Contains(s.Cone.LeftSide))
// Console.WriteLine("bug");
// }
// foreach (var s in leftConeSides) {
// coneSideComparer.SetOperand(s);
// if (!rightConeSides.Contains(s.Cone.RightSide)) {
// Console.WriteLine("bug");
// PrintOutRightSegTree();
// PrintOutLeftSegTree();
//
// ShowLeftTree();
// ShowRightTree();
// }
// }
// }
//
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// void ShowRightTree(params ICurve[] curves) {
// var l = Obstacles.Select(p => new DebugCurve(100, 5, "green", p)).ToList();
// l.AddRange(rightConeSides.Select(s => new DebugCurve(100, 5, "blue",ExtendSegmentToZ(s))));
//
// // foreach (VisibilityEdge edge in visibilityGraph.Edges)
// // l.Add(BezierOnEdge(edge));
//
// l.AddRange(curves.Select(c => new DebugCurve(100, 5, "brown", c)));
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l);
// }
//
//
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "curves"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// void ShowBothTrees(params DebugCurve[] curves) {
// var l = Obstacles.Select(p => new DebugCurve(100, 5, "green", p)).ToList();
// l.AddRange(leftConeSides.Select(s => new DebugCurve(ExtendSegmentToZ(s))));
// l .AddRange(rightConeSides.Select(s => new DebugCurve(ExtendSegmentToZ(s))));
//
// // foreach (VisibilityEdge edge in visibilityGraph.Edges)
// // l.Add(BezierOnEdge(edge));
//
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l);
// }
//
// void ShowLeftTree(params ICurve[] curves) {
// var l = Obstacles.Select(p => new DebugCurve(100, 0.01,"green", p)).ToList();
// var range = new RealNumberSpan();
// var ellipseSize = 0.01;
//
// foreach (var s in leftConeSides) {
// var curve = ExtendSegmentToZ(s);
// range.AddValue(curve.Start*DirectionPerp);
// range.AddValue(curve.End * DirectionPerp);
// l.Add(new DebugCurve(100, 0.1, "red", curve));
// l.Add(new DebugCurve(200,0.1, "black", new Ellipse(ellipseSize,ellipseSize, curve.End)));
// ellipseSize += 2;
// }
// l.Add(DebugSweepLine(range));
//
// // foreach (VisibilityEdge edge in visibilityGraph.Edges)
// // l.Add(BezierOnEdge(edge));
//
// l.AddRange(curves.Select(c => new DebugCurve(100, 0.5, "brown", c)));
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l);
// }
//
// DebugCurve DebugSweepLine(RealNumberSpan range) {
// var ls = new LineSegment(Z * SweepDirection + DirectionPerp * range.Min, Z * SweepDirection + DirectionPerp * range.Max);
// return new DebugCurve(100,0.1,"magenta", ls);
// }
// #endif
void AddConeAndEnqueueEvents(VertexEvent vertexEvent) {
var leftVertexEvent = vertexEvent as LeftVertexEvent;
if (leftVertexEvent != null) {
PolylinePoint nextPoint = vertexEvent.Vertex.NextOnPolyline;
CloseConesAddConeAtLeftVertex(leftVertexEvent, nextPoint);
} else {
var rightVertexEvent = vertexEvent as RightVertexEvent;
if (rightVertexEvent != null) {
PolylinePoint nextPoint = vertexEvent.Vertex.PrevOnPolyline;
CloseConesAddConeAtRightVertex(rightVertexEvent, nextPoint);
} else {
CloseConesAddConeAtLeftVertex(vertexEvent, vertexEvent.Vertex.NextOnPolyline);
CloseConesAddConeAtRightVertex(vertexEvent, vertexEvent.Vertex.PrevOnPolyline);
}
}
}
void CloseConesAddConeAtRightVertex(VertexEvent rightVertexEvent,
PolylinePoint nextVertex) {
var prevSite = rightVertexEvent.Vertex.NextOnPolyline.Point;
var prevZ = prevSite*SweepDirection;
if (ApproximateComparer.Close(prevZ, Z))
RemoveConesClosedBySegment(prevSite, rightVertexEvent.Vertex.Point);
var site = rightVertexEvent.Site;
var coneLp = site + ConeLeftSideDirection;
var coneRp = site + ConeRightSideDirection;
var nextSite = nextVertex.Point;
//SugiyamaLayoutSettings.Show(new LineSegment(site, coneLP), new LineSegment(site, coneRP), new LineSegment(site, nextSite));
//try to remove the right side
if ((site - prevSite)*SweepDirection > ApproximateComparer.DistanceEpsilon)
RemoveRightSide(new RightObstacleSide(rightVertexEvent.Vertex.NextOnPolyline));
if ((site - nextVertex.Point) * SweepDirection > ApproximateComparer.DistanceEpsilon)
RemoveLeftSide(new LeftObstacleSide(nextVertex));
if (GetZ(nextSite) + ApproximateComparer.DistanceEpsilon < GetZ(rightVertexEvent))
CreateConeOnVertex(rightVertexEvent);
if (!Point.PointToTheRightOfLineOrOnLine(nextSite, site, coneLp)) {
//if (angle <= -coneAngle / 2) {
CreateConeOnVertex(rightVertexEvent);
if (Point.PointToTheLeftOfLineOrOnLine(nextSite + DirectionPerp, nextSite, site))
EnqueueEvent(new RightVertexEvent(nextVertex));
// TryEnqueueRighVertexEvent(nextVertex);
} else if (Point.PointToTheLeftOfLineOrOnLine(nextSite, site, coneRp))
//if (angle < coneAngle / 2) {
CaseToTheLeftOfLineOrOnLineConeRp(rightVertexEvent, nextVertex);
else {
if ((nextSite - site)*SweepDirection > ApproximateComparer.DistanceEpsilon) {
LookForIntersectionOfObstacleSideAndLeftConeSide(rightVertexEvent.Site, nextVertex);
InsertRightSide(new RightObstacleSide(rightVertexEvent.Vertex));
}
EnqueueEvent(new RightVertexEvent(nextVertex));
}
}
void CaseToTheLeftOfLineOrOnLineConeRp(VertexEvent rightVertexEvent, PolylinePoint nextVertex) {
EnqueueEvent(new RightVertexEvent(nextVertex));
//the obstacle side is inside of the cone
//we need to create an obstacle left side segment instead of the left cone side
var cone = new Cone(rightVertexEvent.Vertex.Point, this);
var obstacleSideSeg = new BrokenConeSide(cone.Apex, nextVertex, new ConeLeftSide(cone));
cone.LeftSide = obstacleSideSeg;
cone.RightSide = new ConeRightSide(cone);
var rnode = InsertToTree(rightConeSides, cone.RightSide);
LookForIntersectionWithConeRightSide(rnode);
var lnode = InsertToTree(leftConeSides, cone.LeftSide);
FixConeLeftSideIntersections(obstacleSideSeg, lnode);
if ((nextVertex.Point - rightVertexEvent.Site) * SweepDirection > ApproximateComparer.DistanceEpsilon)
InsertRightSide(new RightObstacleSide(rightVertexEvent.Vertex));
}
void LookForIntersectionOfObstacleSideAndRightConeSide(Point obstacleSideStart,
PolylinePoint obstacleSideVertex) {
RBNode node = GetLastNodeToTheLeftOfPointInRightSegmentTree(obstacleSideStart);
if (node != null) {
var coneRightSide = node.Item as ConeRightSide;
if (coneRightSide != null) {
Point intersection;
if (Point.IntervalIntersectsRay(obstacleSideStart, obstacleSideVertex.Point,
coneRightSide.Start, ConeRightSideDirection, out intersection) &&
SegmentIsNotHorizontal(intersection, obstacleSideVertex.Point)) {
EnqueueEvent(CreateRightIntersectionEvent(coneRightSide, intersection, obstacleSideVertex));
// Console.WriteLine("right intersection");
// Show(new Ellipse(3, 3, intersection));
}
}
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
RightIntersectionEvent CreateRightIntersectionEvent(ConeRightSide coneRightSide, Point intersection,
PolylinePoint obstacleSideVertex) {
Debug.Assert(Math.Abs((obstacleSideVertex.Point - intersection) * SweepDirection) > 0);
return new RightIntersectionEvent(coneRightSide,
intersection, obstacleSideVertex);
}
RBNode GetLastNodeToTheLeftOfPointInRightSegmentTree(Point obstacleSideStart) {
return rightConeSides.FindLast(
s => PointIsToTheRightOfSegment(obstacleSideStart, s));
}
void LookForIntersectionOfObstacleSideAndLeftConeSide(Point obstacleSideStart,
PolylinePoint obstacleSideVertex) {
var node = GetFirstNodeToTheRightOfPoint(obstacleSideStart);
// ShowLeftTree(Box(obstacleSideStart));
if (node == null) return;
var coneLeftSide = node.Item as ConeLeftSide;
if (coneLeftSide == null) return;
Point intersection;
if (Point.IntervalIntersectsRay(obstacleSideStart, obstacleSideVertex.Point, coneLeftSide.Start,
ConeLeftSideDirection, out intersection)) {
EnqueueEvent(new LeftIntersectionEvent(coneLeftSide, intersection, obstacleSideVertex));
}
}
RBNode GetFirstNodeToTheRightOfPoint(Point p) {
return leftConeSides.FindFirst(s => PointIsToTheLeftOfSegment(p, s));
}
#if TEST_MSAGL
// ReSharper disable UnusedMember.Local
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
static ICurve Box(Point p) {
// ReSharper restore UnusedMember.Local
return CurveFactory.CreateRectangle(2, 2, p);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",
"CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String)"
)]
void PrintOutRightSegTree() {
Console.WriteLine("right segment tree");
foreach (var t in rightConeSides)
Console.WriteLine(t);
Console.WriteLine("end of right segments");
}
#endif
static bool PointIsToTheLeftOfSegment(Point p, ConeSide seg) {
return (Point.GetTriangleOrientation(seg.Start, seg.Start + seg.Direction, p) ==
TriangleOrientation.Counterclockwise);
}
static bool PointIsToTheRightOfSegment(Point p, ConeSide seg) {
return (Point.GetTriangleOrientation(seg.Start, seg.Start + seg.Direction, p) ==
TriangleOrientation.Clockwise);
}
void FixConeLeftSideIntersections(BrokenConeSide leftSide, RBNode rbNode) {
//the first intersection can happen only with succesors of leftSide
Debug.Assert(rbNode != null);
do { //this loop usually works only once
rbNode = leftConeSides.Next(rbNode);
} while (rbNode != null && Point.PointToTheRightOfLineOrOnLine(leftSide.Start, rbNode.Item.Start, rbNode.Item.Start+rbNode.Item.Direction));
if (rbNode != null) {
Point intersection;
var seg = rbNode.Item as ConeLeftSide;
if (seg != null &&
Point.IntervalIntersectsRay(leftSide.Start, leftSide.End, seg.Start, seg.Direction, out intersection)) {
EnqueueEvent(new LeftIntersectionEvent(seg, intersection, leftSide.EndVertex));
//Show(CurveFactory.CreateDiamond(3, 3, intersection));
}
}
}
RBNode InsertToTree(RbTree tree, ConeSide coneSide) {
Debug.Assert(coneSide.Direction * SweepDirection > 0);
coneSideComparer.SetOperand(coneSide);
return tree.Insert(coneSide);
}
void CloseConesAddConeAtLeftVertex(VertexEvent leftVertexEvent, PolylinePoint nextVertex) {
//close segments first
Point prevSite = leftVertexEvent.Vertex.PrevOnPolyline.Point;
double prevZ = prevSite * SweepDirection;
if (ApproximateComparer.Close(prevZ, Z) && (prevSite - leftVertexEvent.Site) * DirectionPerp > 0) {
//Show(
// new Ellipse(1, 1, prevSite),
// CurveFactory.CreateBox(2, 2, leftVertexEvent.Vertex.Point));
RemoveConesClosedBySegment(leftVertexEvent.Vertex.Point, prevSite);
}
var site = leftVertexEvent.Site;
var coneLp = site + ConeLeftSideDirection;
var coneRp = site + ConeRightSideDirection;
var nextSite = nextVertex.Point;
// SugiyamaLayoutSettings.Show(new LineSegment(site, coneLP), new LineSegment(site, coneRP), new LineSegment(site, nextSite));
if ((site - prevSite) * SweepDirection > ApproximateComparer.DistanceEpsilon)
RemoveLeftSide(new LeftObstacleSide(leftVertexEvent.Vertex.PrevOnPolyline));
var nextDelZ = GetZ(nextSite) - Z;
if(nextDelZ<-ApproximateComparer.DistanceEpsilon)
RemoveRightSide(new RightObstacleSide(nextVertex));
if (nextDelZ < -ApproximateComparer.DistanceEpsilon ||
ApproximateComparer.Close(nextDelZ, 0) && (nextSite - leftVertexEvent.Site) * DirectionPerp > 0) {
//if (angle > Math.PI / 2)
CreateConeOnVertex(leftVertexEvent); //it is the last left vertex on this obstacle
} else if (!Point.PointToTheLeftOfLineOrOnLine(nextSite, site, coneRp)) {
//if (angle >= coneAngle / 2) {
CreateConeOnVertex(leftVertexEvent);
EnqueueEvent(new LeftVertexEvent(nextVertex));
//we schedule LeftVertexEvent for a vertex with horizontal segment to the left on the top of the obstace
} else if (!Point.PointToTheLeftOfLineOrOnLine(nextSite, site, coneLp)) {
//if (angle >= -coneAngle / 2) {
//we cannot completely obscure the cone here
EnqueueEvent(new LeftVertexEvent(nextVertex));
//the obstacle side is inside of the cone
//we need to create an obstacle right side segment instead of the cone side
var cone = new Cone(leftVertexEvent.Vertex.Point, this);
var rightSide = new BrokenConeSide(leftVertexEvent.Vertex.Point, nextVertex,
new ConeRightSide(cone));
cone.RightSide = rightSide;
cone.LeftSide = new ConeLeftSide(cone);
LookForIntersectionWithConeLeftSide(InsertToTree(leftConeSides, cone.LeftSide));
var rbNode = InsertToTree(rightConeSides, rightSide);
FixConeRightSideIntersections(rightSide, rbNode);
if ((nextVertex.Point - leftVertexEvent.Site) * SweepDirection > ApproximateComparer.DistanceEpsilon)
InsertLeftSide(new LeftObstacleSide(leftVertexEvent.Vertex));
} else {
EnqueueEvent(new LeftVertexEvent(nextVertex));
if ((nextVertex.Point - leftVertexEvent.Site) * SweepDirection > ApproximateComparer.DistanceEpsilon) {
//if( angle >- Pi/2
// Debug.Assert(angle > -Math.PI / 2);
LookForIntersectionOfObstacleSideAndRightConeSide(leftVertexEvent.Site, nextVertex);
InsertLeftSide(new LeftObstacleSide(leftVertexEvent.Vertex));
}
}
}
void RemoveCone(Cone cone)
{
// the following should not happen if the containment hierarchy is correct.
// If containment is not correct it still should not result in a fatal error, just a funny looking route.
// Debug.Assert(cone.Removed == false);
cone.Removed = true;
RemoveSegFromLeftTree(cone.LeftSide);
RemoveSegFromRightTree(cone.RightSide);
}
void RemoveSegFromRightTree(ConeSide coneSide) {
// ShowRightTree();
Debug.Assert(coneSide.Removed == false);
coneSideComparer.SetOperand(coneSide);
var b = rightConeSides.Remove(coneSide);
coneSide.Removed = true;
if (b == null) {
var tmpZ = Z;
Z = Math.Max(GetZ(coneSide.Start), Z - 0.01);
//we need to return to the past a little bit when the order was still correc
coneSideComparer.SetOperand(coneSide);
b = rightConeSides.Remove(coneSide);
Z = tmpZ;
#if TEST_MSAGL
if (b == null) {
PrintOutRightSegTree();
Console.WriteLine(coneSide);
// ShowBothTrees(new DebugCurve("red", CurveFactory.CreateDiamond(3, 4, coneSide.Start)));
// GeometryGraph gg = CreateGraphFromObstacles(Obstacles);
//gg.Save("c:\\tmp\\bug1");
}
#endif
}
// the following should not happen if the containment hierarchy is correct.
// If containment is not correct it still should not result in a fatal error, just a funny looking route.
// Debug.Assert(b != null);
}
void RemoveSegFromLeftTree(ConeSide coneSide) {
Debug.Assert(coneSide.Removed == false);
coneSide.Removed = true;
coneSideComparer.SetOperand(coneSide);
var b = leftConeSides.Remove(coneSide);
if (b == null) {
var tmpZ = Z;
Z = Math.Max(GetZ(coneSide.Start), Z - 0.01);
coneSideComparer.SetOperand(coneSide);
#if TEST_MSAGL
b =
#endif
leftConeSides.Remove(coneSide);
Z = tmpZ;
// #if TEST_MSAGL
// if (b == null) {
// PrintOutLeftSegTree();
// Console.WriteLine(coneSide);
// ShowLeftTree(new Ellipse(2, 2, coneSide.Start));
// }
// #endif
}
Debug.Assert(b != null);
}
///
///
///
///
/// represents a node of the right cone side
void FixConeRightSideIntersections(BrokenConeSide rightSide, RBNode rbNode) {
//the first intersection can happen only with predecessors of rightSide
Debug.Assert(rbNode != null);
do { //this loop usually works only once
rbNode = rightConeSides.Previous(rbNode);
} while (rbNode != null && Point.PointToTheLeftOfLineOrOnLine(rightSide.Start, rbNode.Item.Start, rbNode.Item.Start + rbNode.Item.Direction));
if (rbNode != null) {
Point intersection;
var seg = rbNode.Item as ConeRightSide;
if (seg != null &&
Point.IntervalIntersectsRay(rightSide.Start, rightSide.End, seg.Start, seg.Direction,
out intersection)) {
EnqueueEvent(CreateRightIntersectionEvent(seg, intersection, rightSide.EndVertex));
// Show(CurveFactory.CreateDiamond(3, 3, intersection));
}
}
}
void CreateConeOnVertex(SweepEvent sweepEvent) {
var cone = new Cone(sweepEvent.Site, this);
#if SHARPKIT //http://code.google.com/p/sharpkit/issues/detail?id=368
//SharpKit/Colin - property assignment values not retained
cone.LeftSide = new ConeLeftSide(cone);
cone.RightSide = new ConeRightSide(cone);
var leftNode = InsertToTree(leftConeSides, cone.LeftSide);
var rightNode = InsertToTree(rightConeSides, cone.RightSide);
#else
var leftNode = InsertToTree(leftConeSides, cone.LeftSide = new ConeLeftSide(cone));
var rightNode = InsertToTree(rightConeSides, cone.RightSide = new ConeRightSide(cone));
#endif
LookForIntersectionWithConeRightSide(rightNode);
LookForIntersectionWithConeLeftSide(leftNode);
}
void LookForIntersectionWithConeLeftSide(RBNode leftNode) {
//Show(new Ellipse(1, 1, leftNode.item.Start));
var coneLeftSide = leftNode.Item as ConeLeftSide;
if (coneLeftSide != null) {
//leftNode = leftSegmentTree.TreePredecessor(leftNode);
//if (leftNode != null) {
// var seg = leftNode.item as ObstacleSideSegment;
// if (seg != null)
// TryIntersectionOfConeLeftSideAndObstacleConeSide(coneLeftSide, seg);
//}
RightObstacleSide rightObstacleSide = FindFirstObstacleSideToTheLeftOfPoint(coneLeftSide.Start);
if (rightObstacleSide != null)
TryIntersectionOfConeLeftSideAndObstacleSide(coneLeftSide, rightObstacleSide);
} else {
var seg = (BrokenConeSide)leftNode.Item;
leftNode = leftConeSides.Next(leftNode);
if (leftNode != null) {
coneLeftSide = leftNode.Item as ConeLeftSide;
if (coneLeftSide != null)
TryIntersectionOfConeLeftSideAndObstacleConeSide(coneLeftSide, seg);
}
}
}
void LookForIntersectionWithConeRightSide(RBNode rightNode) {
//Show(new Ellipse(10, 5, rightNode.item.Start));
var coneRightSide = rightNode.Item as ConeRightSide;
if (coneRightSide != null) {
//rightNode = rightSegmentTree.TreeSuccessor(rightNode);
//if (rightNode != null) {
// var seg = rightNode.item as ObstacleSideSegment;
// if (seg != null)
// TryIntersectionOfConeRightSideAndObstacleConeSide(coneRightSide, seg);
//}
LeftObstacleSide leftObstacleSide = FindFirstObstacleSideToToTheRightOfPoint(coneRightSide.Start);
if (leftObstacleSide != null)
TryIntersectionOfConeRightSideAndObstacleSide(coneRightSide, leftObstacleSide);
} else {
var seg = (BrokenConeSide)rightNode.Item;
rightNode = rightConeSides.Previous(rightNode);
if (rightNode != null) {
coneRightSide = rightNode.Item as ConeRightSide;
if (coneRightSide != null)
TryIntersectionOfConeRightSideAndObstacleConeSide(coneRightSide, seg);
}
}
}
void TryIntersectionOfConeRightSideAndObstacleConeSide(ConeRightSide coneRightSide,
BrokenConeSide seg) {
Point x;
if (Point.IntervalIntersectsRay(seg.Start, seg.End, coneRightSide.Start,
coneRightSide.Direction, out x)) {
EnqueueEvent(CreateRightIntersectionEvent(coneRightSide, x, seg.EndVertex));
//Show(CurveFactory.CreateDiamond(3, 3, x));
}
}
void TryIntersectionOfConeRightSideAndObstacleSide(ConeRightSide coneRightSide, ObstacleSide side) {
Point x;
if (Point.IntervalIntersectsRay(side.Start, side.End, coneRightSide.Start,
coneRightSide.Direction, out x)) {
EnqueueEvent(CreateRightIntersectionEvent(coneRightSide, x, side.EndVertex));
//Show(CurveFactory.CreateDiamond(3, 3, x));
}
}
void TryIntersectionOfConeLeftSideAndObstacleConeSide(ConeLeftSide coneLeftSide, BrokenConeSide seg) {
Point x;
if (Point.IntervalIntersectsRay(seg.Start, seg.End, coneLeftSide.Start, coneLeftSide.Direction, out x)) {
EnqueueEvent(new LeftIntersectionEvent(coneLeftSide, x, seg.EndVertex));
//Show(CurveFactory.CreateDiamond(3, 3, x));
}
}
void TryIntersectionOfConeLeftSideAndObstacleSide(ConeLeftSide coneLeftSide, ObstacleSide side) {
Point x;
if (Point.IntervalIntersectsRay(side.Start, side.End, coneLeftSide.Start, coneLeftSide.Direction, out x)) {
EnqueueEvent(new LeftIntersectionEvent(coneLeftSide, x, side.EndVertex));
// Show(CurveFactory.CreateDiamond(3, 3, x));
}
}
// #if TEST_MSAGL
// internal void Show(params ICurve[] curves) {
// var l = Obstacles.Select(o => new DebugCurve(100, 0.1, "blue", o)).ToList();
//
// foreach (var s in rightConeSides) {
// l.Add(new DebugCurve(0.5, "brown", ExtendSegmentToZ(s)));
// if (s is BrokenConeSide)
// l.Add(new DebugCurve("brown", Diamond(s.Start)));
// l.Add(new DebugCurve(0.5, "green",
// ExtendSegmentToZ(s.Cone.LeftSide)));
// if (s.Cone.LeftSide is BrokenConeSide)
// l.Add(new DebugCurve("green", Diamond(s.Cone.LeftSide.Start)));
// }
//
// // l.AddRange(
// // visibilityGraph.Edges.Select(
// // edge => new DebugCurve(0.2, "maroon", new LineSegment(edge.SourcePoint, edge.TargetPoint))));
//
// l.AddRange(curves.Select(c => new DebugCurve("red", c)));
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l);
// }
//
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// static CubicBezierSegment BezierOnEdge(VisibilityEdge edge) {
// return new CubicBezierSegment(edge.SourcePoint, 2.0 / 3.0 * edge.SourcePoint + 1.0 / 3.0 * edge.TargetPoint,
// 1.0 / 3.0 * edge.SourcePoint + 2.0 / 3.0 * edge.TargetPoint, edge.TargetPoint);
// }
//
// internal ICurve ExtendSegmentToZ(ConeSide segment) {
// double den = segment.Direction * SweepDirection;
// Debug.Assert(Math.Abs(den) > ApproximateComparer.DistanceEpsilon);
// double t = (Z + 40 - segment.Start * SweepDirection) / den;
// return new LineSegment(segment.Start, segment.Start + segment.Direction * t);
// }
//
// internal ICurve ExtendSegmentToZPlus1(ConeSide segment) {
// double den = segment.Direction * SweepDirection;
// Debug.Assert(Math.Abs(den) > ApproximateComparer.DistanceEpsilon);
// double t = (Z + 1 - segment.Start * SweepDirection) / den;
//
// return new LineSegment(segment.Start, segment.Start + segment.Direction * t);
// }
// #endif
// static int count;
void GoOverConesSeeingVertexEvent(SweepEvent vertexEvent) {
var rbNode = FindFirstSegmentInTheRightTreeNotToTheLeftOfVertex(vertexEvent);
if (rbNode == null) return;
var coneRightSide = rbNode.Item;
var cone = coneRightSide.Cone;
var leftConeSide = cone.LeftSide;
if (VertexIsToTheLeftOfSegment(vertexEvent, leftConeSide)) return;
var visibleCones = new List { cone };
coneSideComparer.SetOperand(leftConeSide);
rbNode = leftConeSides.Find(leftConeSide);
if (rbNode == null) {
var tmpZ = Z;
Z = Math.Max(GetZ(leftConeSide.Start), PreviousZ);
//we need to return to the past a little bit when the order was still correct
coneSideComparer.SetOperand(leftConeSide);
rbNode = leftConeSides.Find(leftConeSide);
Z = tmpZ;
// #if TEST_MSAGL
// // if (rbNode == null) {
// //GeometryGraph gg = CreateGraphFromObstacles();
// //gg.Save("c:\\tmp\\bug");
// // PrintOutLeftSegTree();
// // Console.WriteLine(leftConeSide);
// // ShowLeftTree(new Ellipse(3, 3, vertexEvent.Site));
// // ShowRightTree(new Ellipse(3, 3, vertexEvent.Site));
// // }
// #endif
}
// the following should not happen if the containment hierarchy is correct.
// If containment is not correct it still should not result in a fatal error, just a funny looking route.
// Debug.Assert(rbNode!=null);
if (rbNode == null) {//it is an emergency measure and should not happen
rbNode = GetRbNodeEmergency(rbNode, leftConeSide);
}
if (rbNode == null)
return; // the cone is not there! and it is a bug
rbNode = leftConeSides.Next(rbNode);
while (rbNode != null && !VertexIsToTheLeftOfSegment(vertexEvent, rbNode.Item)) {
visibleCones.Add(rbNode.Item.Cone);
rbNode = leftConeSides.Next(rbNode);
}
//Show(new Ellipse(1, 1, vertexEvent.Site));
foreach (var visCone in visibleCones)
AddEdgeAndRemoveCone(visCone, vertexEvent.Site);
}
RBNode GetRbNodeEmergency(RBNode rbNode, ConeSide leftConeSide) {
for (var node = leftConeSides.TreeMinimum(); node != null; node = leftConeSides.Next(node))
if (node.Item == leftConeSide) {
rbNode = node;
break;
}
return rbNode;
}
// #if TEST_MSAGL
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider",
// MessageId = "System.Int32.ToString")]
// internal static GeometryGraph CreateGraphFromObstacles(IEnumerable obstacles) {
// var gg = new GeometryGraph();
// foreach (var ob in obstacles) {
// gg.Nodes.Add(new Node(ob.ToCurve()));
// }
// return gg;
// }
//
// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String,System.Object,System.Object)"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",
// "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String)"
// )]
// void PrintOutLeftSegTree() {
// Console.WriteLine("Left cone segments########");
// foreach (var t in leftConeSides) {
// var x = coneSideComparer.IntersectionOfSegmentAndSweepLine(t);
//
// Console.WriteLine("{0} x={1}", t, x*DirectionPerp );
// }
// Console.WriteLine("##########end of left cone segments");
// }
// #endif
static bool VertexIsToTheLeftOfSegment(SweepEvent vertexEvent, ConeSide seg) {
return (Point.GetTriangleOrientation(seg.Start, seg.Start + seg.Direction,
vertexEvent.Site) == TriangleOrientation.Counterclockwise);
}
static bool VertexIsToTheRightOfSegment(SweepEvent vertexEvent, ConeSide seg) {
return (Point.GetTriangleOrientation(seg.Start, seg.Start + seg.Direction,
vertexEvent.Site) == TriangleOrientation.Clockwise);
}
RBNode FindFirstSegmentInTheRightTreeNotToTheLeftOfVertex(SweepEvent vertexEvent) {
return rightConeSides.FindFirst(
s => !VertexIsToTheRightOfSegment(vertexEvent, s)
);
}
void EnqueueEvent(RightVertexEvent vertexEvent) {
if (SweepDirection * (vertexEvent.Site - vertexEvent.Vertex.PrevOnPolyline.Point) > ApproximateComparer.Tolerance)
return;//otherwise we enqueue the vertex twice; once as a LeftVertexEvent and once as a RightVertexEvent
base.EnqueueEvent(vertexEvent);
}
}
}