using System;
using System.Collections.Generic;
using Microsoft.Msagl.Core.DataStructures;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Geometry.Curves;
namespace Microsoft.Msagl.Routing.Visibility {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
internal class TangentVisibilityGraphCalculator {
///
/// the list of obstacles
///
List polygons;
VisibilityGraph visibilityGraph;
List diagonals;
List tangents;
RbTree activeDiagonalTree;
Polygon currentPolygon;
ActiveDiagonalComparerWithRay activeDiagonalComparer = new ActiveDiagonalComparerWithRay();
bool useLeftPTangents;
internal static void AddTangentVisibilityEdgesToGraph(List holes, VisibilityGraph visibilityGraph) {
if (holes.Count > 1) {
TangentVisibilityGraphCalculator calculator = new TangentVisibilityGraphCalculator(holes, visibilityGraph, true);
calculator.CalculateAndAddEdges();
//use another family of tangents
calculator = new TangentVisibilityGraphCalculator(holes, visibilityGraph, false);
calculator.CalculateAndAddEdges();
}
}
private void CalculateAndAddEdges() {
for (int i = 0; i < polygons.Count; i++)
CalculateVisibleTangentsFromPolygon(i);
}
private void CalculateVisibleTangentsFromPolygon(int i) {
AllocateDataStructures(i);
OrganizeTangents();
InitActiveDiagonals();
Sweep();
}
private void AllocateDataStructures(int i) {
tangents = new List();
diagonals = new List();
activeDiagonalTree = new RbTree(this.activeDiagonalComparer);
this.currentPolygon = polygons[i];
}
private void Sweep() {
if (tangents.Count < 2)
return;
for (int i = 1; i < tangents.Count; i++) { //we processed the first element already
Tangent t = tangents[i];
if (t.Diagonal != null) {
if (t.Diagonal.RbNode == activeDiagonalTree.TreeMinimum())
AddVisibleEdge(t);
if (t.IsHigh)
RemoveDiagonalFromActiveNodes(t.Diagonal);
} else {
if (t.IsLow) {
this.activeDiagonalComparer.PointOnTangentAndInsertedDiagonal = t.End.Point;
this.InsertActiveDiagonal(new Diagonal(t, t.Comp));
if (t.Diagonal.RbNode == activeDiagonalTree.TreeMinimum())
AddVisibleEdge(t);
}
}
#if TEST_MSAGL
//List cs = new List();
//foreach (Diagonal d in this.activeDiagonalTree) {
// cs.Add(new LineSegment(d.Start, d.End));
//}
//foreach (Polygon p in this.polygons)
// cs.Add(p.Polyline);
//cs.Add(new LineSegment(t.Start.Point, t.End.Point));
//SugiyamaLayoutSettings.Show(cs.ToArray);
#endif
}
}
private void AddVisibleEdge(Tangent t) {
VisibilityGraph.AddEdge(visibilityGraph.GetVertex(t.Start), visibilityGraph.GetVertex(t.End));
}
///
/// this function will also add the first tangent to the visible edges if needed
///
private void InitActiveDiagonals() {
Tangent firstTangent = this.tangents[0];
Point firstTangentStart = firstTangent.Start.Point;
Point firstTangentEnd = firstTangent.End.Point;
foreach (Diagonal diagonal in diagonals) {
if (RayIntersectDiagonal(firstTangentStart, firstTangentEnd, diagonal)) {
this.activeDiagonalComparer.PointOnTangentAndInsertedDiagonal =
ActiveDiagonalComparerWithRay.IntersectDiagonalWithRay(firstTangentStart, firstTangentEnd, diagonal);
InsertActiveDiagonal(diagonal);
}
}
if (firstTangent.Diagonal.RbNode == this.activeDiagonalTree.TreeMinimum())
AddVisibleEdge(firstTangent);
if (firstTangent.IsLow == false) { //remove the diagonal of the top tangent from active edges
Diagonal diag = firstTangent.Diagonal;
RemoveDiagonalFromActiveNodes(diag);
}
}
#if DEBUG && TEST_MSAGL
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
private void AddPolylinesForShow(List curves) {
foreach (Polygon p in this.polygons)
curves.Add(p.Polyline);
}
#endif
private void RemoveDiagonalFromActiveNodes(Diagonal diag) {
RBNode changedNode = activeDiagonalTree.DeleteSubtree(diag.RbNode);
if (changedNode != null)
if (changedNode.Item != null)
changedNode.Item.RbNode = changedNode;
diag.LeftTangent.Diagonal = null;
diag.RightTangent.Diagonal = null;
}
private void InsertActiveDiagonal(Diagonal diagonal) {
diagonal.RbNode = activeDiagonalTree.Insert(diagonal);
MarkDiagonalAsActiveInTangents(diagonal);
}
private static void MarkDiagonalAsActiveInTangents(Diagonal diagonal) {
diagonal.LeftTangent.Diagonal = diagonal;
diagonal.RightTangent.Diagonal = diagonal;
}
static bool RayIntersectDiagonal(Point pivot, Point pointOnRay, Diagonal diagonal) {
Point a = diagonal.Start;
Point b = diagonal.End;
return Point.GetTriangleOrientation(pivot, a, b) == TriangleOrientation.Counterclockwise
&&
Point.GetTriangleOrientation(pivot, pointOnRay, a) != TriangleOrientation.Counterclockwise
&&
Point.GetTriangleOrientation(pivot, pointOnRay, b) != TriangleOrientation.Clockwise;
}
///
/// compare tangents by measuring the counterclockwise angle between the tangent and the edge
///
///
///
///
int TangentComparison(Tangent e0, Tangent e1) {
return StemStartPointComparer.CompareVectorsByAngleToXAxis(e0.End.Point - e0.Start.Point, e1.End.Point - e1.Start.Point);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "cc")]
private void OrganizeTangents() {
foreach (Polygon q in polygons)
if (q != this.currentPolygon)
ProcessPolygonQ(q);
this.tangents.Sort(new Comparison(TangentComparison));
/////debug
//List cc = new List();
//foreach (Tangent t in this.tangents)
// cc.Add(new LineSegment(t.Start.Point, t.End.Point));
//foreach (Polygon p in polygons)
// cc.Add(p.Polyline);
//SugiyamaLayoutSettings.Show(cc.ToArray());
////end debug
}
private void ProcessPolygonQ(Polygon q) {
TangentPair tangentPair = new TangentPair(currentPolygon, q);
if (this.useLeftPTangents)
tangentPair.CalculateLeftTangents();
else
tangentPair.CalculateRightTangents();
Tuple couple = useLeftPTangents ? tangentPair.leftPLeftQ : tangentPair.rightPLeftQ;
Tangent t0 = new Tangent(currentPolygon[couple.Item1], q[couple.Item2]);
t0.IsLow = true;
t0.SeparatingPolygons = !this.useLeftPTangents;
couple = useLeftPTangents ? tangentPair.leftPRightQ : tangentPair.rightPRightQ;
Tangent t1 = new Tangent(currentPolygon[couple.Item1], q[couple.Item2]);
t1.IsLow = false;
t1.SeparatingPolygons = this.useLeftPTangents;
t0.Comp = t1;
t1.Comp = t0;
this.tangents.Add(t0);
this.tangents.Add(t1);
this.diagonals.Add(new Diagonal(t0, t1));
}
TangentVisibilityGraphCalculator(List holes, VisibilityGraph visibilityGraph, bool useLeftPTangents) {
this.polygons = holes;
this.visibilityGraph = visibilityGraph;
this.useLeftPTangents = useLeftPTangents;
}
internal delegate bool FilterVisibleEdgesDelegate(Point a, Point b);
}
}