using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Msagl.Core.DataStructures; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Geometry.Curves; using Microsoft.Msagl.Core.Routing; using Microsoft.Msagl.Routing.ConstrainedDelaunayTriangulation; namespace Microsoft.Msagl.Routing.Spline.Bundling { /// /// Check intersections between edges and obstacles using triangulation (faster than kd-tree) /// internal class CdtIntersections { readonly MetroGraphData metroGraphData; readonly BundlingSettings bundlingSettings; internal bool ComputeForcesForBundles = false; public CdtIntersections(MetroGraphData metroGraphData, BundlingSettings bundlingSettings) { this.metroGraphData = metroGraphData; this.bundlingSettings = bundlingSettings; } /// /// returns false iff the edge overlap an obstacle /// otherwise it calulates distances to the closest obstacles /// internal bool BundleAvoidsObstacles(Station v, Station u, Point vPosition, Point uPosition, double upperBound, out List> closestDist) { closestDist = new List>(); //return true; Set obstaclesToIgnore = metroGraphData.looseIntersections.ObstaclesToIgnoreForBundle(v, u); Dictionary> closeObstacles = FindCloseObstaclesForBundle(u.CdtTriangle, uPosition, vPosition, obstaclesToIgnore, upperBound); if (closeObstacles == null) return false; //Polyline bundle = Intersections.Create4gon(vPosition, uPosition, upperBound, upperBound); foreach (var item in closeObstacles) { Tuple dist = item.Value; //TODO: get rif od this call! //if (!Intersections.ClosedPolylinesIntersect(bundle, obstacle)) continue; //if (!Curve.ClosedCurveInteriorsIntersect(bundle, obstacle)) continue; closestDist.Add(dist); } return true; } /// /// returns null iff the edge overlap an obstacle /// Dictionary> FindCloseObstaclesForBundle(CdtTriangle startTriangle, Point start, Point end, Set obstaclesToIgnore, double upperBound) { var obstacles = new Dictionary>(); List list; if (!ThreadLineSegmentThroughTriangles(startTriangle, start, end, obstaclesToIgnore, out list)) return null; if (!ComputeForcesForBundles && !bundlingSettings.HighestQuality) return obstacles; var checkedSites = new HashSet(); foreach (var t in list) { foreach (var s in t.Sites) { if (!checkedSites.Add(s)) continue; var poly = (Polyline) s.Owner; if (obstaclesToIgnore.Contains(poly)) continue; PolylinePoint pp = FindPolylinePoint(poly, s.Point); double par11, par12, par21, par22; double d12 = LineSegment.MinDistBetweenLineSegments(pp.Point, pp.NextOnPolyline.Point, start, end, out par11, out par12); double d22 = LineSegment.MinDistBetweenLineSegments(pp.Point, pp.PrevOnPolyline.Point, start, end, out par21, out par22); Point r1, r2; double dist; if (d12 < d22) { dist = d12; if (dist > upperBound) continue; r1 = pp.Point + (pp.NextOnPolyline.Point - pp.Point)*par11; r2 = start + (end - start)*par12; } else { dist = d22; if (dist > upperBound) continue; r1 = pp.Point + (pp.PrevOnPolyline.Point - pp.Point)*par21; r2 = start + (end - start)*par22; } //if (dist > upperBound) continue; Tuple currentValue; if (!obstacles.TryGetValue(poly, out currentValue)) obstacles.Add(poly, new Tuple(r1, r2)); } } return obstacles; } /// /// returns false iff the edge overlap an obstacle /// bool ThreadLineSegmentThroughTriangles(CdtTriangle currentTriangle, Point start, Point end, Set obstaclesToIgnore, out List triangles) { Debug.Assert(Cdt.PointIsInsideOfTriangle(start, currentTriangle)); triangles = new List(); if (Cdt.PointIsInsideOfTriangle(end, currentTriangle)) { triangles.Add(currentTriangle); return true; } var threader = new CdtThreader(currentTriangle, start, end); triangles.Add(currentTriangle); while (threader.MoveNext()) { triangles.Add(threader.CurrentTriangle); var piercedEdge = threader.CurrentPiercedEdge; if (piercedEdge.Constrained) { Debug.Assert(piercedEdge.lowerSite.Owner == piercedEdge.upperSite.Owner); var poly = (Polyline) piercedEdge.lowerSite.Owner; if (!obstaclesToIgnore.Contains(poly)) return false; } } if (threader.CurrentTriangle != null) triangles.Add(threader.CurrentTriangle); // // int positiveSign, negativeSign; // CdtEdge piercedEdge = FindFirstPiercedEdge(currentTriangle, start, end, out negativeSign, out positiveSign, null); // // Debug.Assert(positiveSign > negativeSign); // // Debug.Assert(piercedEdge != null); // // do { // triangles.Add(currentTriangle); // if (piercedEdge.Constrained) { // Debug.Assert(piercedEdge.lowerSite.Owner == piercedEdge.upperSite.Owner); // Polyline poly = (Polyline)piercedEdge.lowerSite.Owner; // if (!obstaclesToIgnore.Contains(poly)) return false; // } // } // while (FindNextPierced(start, end, ref currentTriangle, ref piercedEdge, ref negativeSign, ref positiveSign)); // if (currentTriangle != null) // triangles.Add(currentTriangle); return true; } internal static PointLocation PointLocationInsideTriangle(Point p, CdtTriangle triangle) { bool seenBoundary=false; for (int i = 0; i < 3; i++) { var area = Point.SignedDoubledTriangleArea(p, triangle.Sites[i].Point, triangle.Sites[i + 1].Point); if (area < -ApproximateComparer.DistanceEpsilon) return PointLocation.Outside; if (area < ApproximateComparer.DistanceEpsilon) seenBoundary = true; } return seenBoundary ? PointLocation.Boundary : PointLocation.Inside; } static PolylinePoint FindPolylinePoint(Polyline poly, Point point) { foreach (var ppp in poly.PolylinePoints) if (ppp.Point == point) return ppp; throw new NotSupportedException(); } /// /// checks if an edge intersects obstacles /// otherwise it calulates distances to the closest obstacles /// internal bool EdgeIsLegal(Station v, Station u, Point vPosition, Point uPosition) { List list; Set obstaclesToIgnore = metroGraphData.looseIntersections.ObstaclesToIgnoreForBundle(v, u); return ThreadLineSegmentThroughTriangles(v.CdtTriangle, vPosition, uPosition, obstaclesToIgnore, out list); } /// /// checks if an edge intersects obstacles /// otherwise it calulates distances to the closest obstacles /// internal bool EdgeIsLegal(Station v, Station u, Point vPosition, Point uPosition, Set obstaclesToIgnore) { var start = v.Position; CdtTriangle currentTriangle = v.CdtTriangle; Debug.Assert(Cdt.PointIsInsideOfTriangle(start, currentTriangle)); Point end = u.Position; if (Cdt.PointIsInsideOfTriangle(end, currentTriangle)) { return true; } var threader = new CdtThreader(currentTriangle, start, end); while (threader.MoveNext()) { var piercedEdge = threader.CurrentPiercedEdge; if (piercedEdge.Constrained) { Debug.Assert(piercedEdge.lowerSite.Owner == piercedEdge.upperSite.Owner); var poly = (Polyline)piercedEdge.lowerSite.Owner; if (!obstaclesToIgnore.Contains(poly)) return false; } } return true; } } }