//  Check intersections between hubs and obstacles with kd-tree
import { Point } from '../../..';
import { Polyline, PointLocation, Curve, GeomConstants } from '../../../math/geometry';
import { uniteSets } from '../../../utils/setOperations';
export class Intersections {
    constructor(metroGraphData, bundlingSettings, obstacleTree, obstaclesToIgnore) {
        this.metroGraphData = metroGraphData;
        this.obstaclesToIgnore = obstaclesToIgnore;
        this.bundlingSettings = bundlingSettings;
        this.obstacleTree = obstacleTree;
    }
    ObstaclesToIgnoreForBundle(node, adj) {
        if (node != null && adj != null) {
            return uniteSets(this.obstaclesToIgnore(node), this.obstaclesToIgnore(adj));
        }
        if (node == null && adj == null) {
            return new Set();
        }
        if (node != null) {
            return this.obstaclesToIgnore(node);
        }
        else {
            return this.obstaclesToIgnore(adj);
        }
    }
    HubAvoidsObstaclesSPNBA(node, center, upperBound, t) {
        // BA stands for boxed array
        t.touchedObstacles = new Array();
        const md = { minimalDistance: upperBound };
        return Intersections.IntersectCircleWithTree(this.obstacleTree, center, upperBound, this.obstaclesToIgnore(node), t.touchedObstacles, md);
    }
    HubAvoidsObstaclesPNS__(center, upperBound, obstaclesToIgnore) {
        const t = { touchedObstacles: Array() };
        const md = { minimalDistance: 0 };
        return this.HubAvoidsObstaclesPNSTT(center, upperBound, obstaclesToIgnore, t, md);
    }
    GetMinimalDistanceToObstacles(node, nodePosition, upperBound) {
        const touchedObstacles = new Array();
        const t = { minimalDistance: upperBound };
        if (!Intersections.IntersectCircleWithTree(this.obstacleTree, nodePosition, upperBound, this.obstaclesToIgnore(node), touchedObstacles, t)) {
            return 0;
        }
        return t.minimalDistance;
    }
    HubAvoidsObstaclesPNSTT(center, upperBound, obstaclesToIgnore, t, m) {
        t.touchedObstacles = new Array();
        m.minimalDistance = upperBound;
        return Intersections.IntersectCircleWithTree(this.obstacleTree, center, upperBound, obstaclesToIgnore, t.touchedObstacles, m);
    }
    //  Computes the intersection between the hub and obstacles
    static IntersectCircleWithTree(node, center, radius, obstaclesToIgnore, touchedObstacles, t) {
        if (!node.irect.contains_point_radius(center, radius)) {
            return true;
        }
        if (node.UserData == null) {
            let res = Intersections.IntersectCircleWithTree(node.Left, center, radius, obstaclesToIgnore, touchedObstacles, t);
            if (!res) {
                return false;
            }
            res = Intersections.IntersectCircleWithTree(node.Right, center, radius, obstaclesToIgnore, touchedObstacles, t);
            if (!res) {
                return false;
            }
        }
        else {
            const obstacle = node.UserData;
            if (obstaclesToIgnore.has(obstacle)) {
                return true;
            }
            const pl = Curve.PointRelativeToCurveLocation(center, obstacle);
            if (pl != PointLocation.Outside) {
                return false;
            }
            const touchPoint = obstacle.value(obstacle.closestParameter(center));
            const dist = touchPoint.sub(center).length;
            if (dist <= radius) {
                touchedObstacles.push([obstacle, touchPoint]);
            }
            t.minimalDistance = Math.min(dist, t.minimalDistance);
        }
        return true;
    }
    static LineSegmentIntersectPolyline(start, end, poly) {
        const segDirection = end.sub(start); // the segment direction vector
        //Debug.Assert(segDirection.Length > ApproximateComparer.DistanceEpsilon);
        let tStart = 0; // the maximum entering segment parameter
        let tEnd = 1; // the minimum leaving segment parameter
        for (const p of poly.polylinePoints()) {
            //process one edge
            const prev = p.prevOnPolyline;
            const e = prev.point.sub(p.point);
            const num = Point.crossProduct(e, start.sub(p.point));
            const den = -Point.crossProduct(e, segDirection);
            if (Math.abs(den) < GeomConstants.tolerance) {
                // segment is nearly parallel to this edge
                if (num < 0)
                    return false;
                continue;
            }
            //intersection parameter
            const t = num / den;
            if (den < 0) {
                // segment is entering across this edge
                tStart = Math.max(tStart, t);
                // segment enters after leaving polygon
                if (tStart > tEnd)
                    return false;
            }
            else {
                // segment is leaving across this edge
                tEnd = Math.min(tEnd, t);
                // segment leaves before entering polygon
                if (tStart > tEnd)
                    return false;
            }
        }
        return true;
    }
    static Create4gon(apex, baseCenter, width1, width2) {
        let norm = baseCenter.sub(apex).normalize();
        norm = new Point(norm.y, norm.x * -1);
        return Polyline.mkFromPoints([
            apex.add(norm.mul(width1 / 2)),
            apex.sub(norm.mul(width1 / 2)),
            baseCenter.sub(norm.mul(width2 / 2)),
            baseCenter.add(norm.mul(width2 / 2)),
        ]);
    }
}
//#if TEST_MSAGL && TEST_MSAGL
// check the validness of the drawing:
//     // 1. hubs are not inside loose obstacles
//     // 2. bundles do not cross loose obstacles
//     // <
//      bool HubPositionsAreOK() {
//         //check polylines
//         foreach(var line of metroGraphData.Metrolines) {
//             var poly = line.Polyline;
//             foreach(var p of poly.PolylinePoints)
//             Assert.assert(metroGraphData.PointToStations.ContainsKey(p.point));
//         }
//         foreach(var station of metroGraphData.Stations) {
//             if (!station.IsRealNode && !HubAvoidsObstacles(station.Position, 0, obstaclesToIgnore(station))) {
//                 if (LayoutAlgorithmSettings.ShowDebugCurvesEnumeration != null) {
//                     HubDebugger.ShowHubs(metroGraphData, bundlingSettings, station);
//                     ShowStationWithObstaclesToIgnore(station, obstacleTree.AllHitItems(station.Position));
//                 }
//                 return false;
//             }
//             //bundles
//             foreach(var adj of station.Neighbors) {
//                 if (Point.closeDistEps(adj.Position, station.Position))
//                     return false;
//                 if (!EdgeIsLegal(station, adj, station.Position, adj.Position)) {
//                     if (LayoutAlgorithmSettings.ShowDebugCurvesEnumeration != null) {
//                         //debug visualization
//                         var l = new Array<DebugCurve>();
//                         //foreach (var st of metroGraphData.Stations) {
//                         //    l.Add(new DebugCurve(100, 0.5, "grey", st.BoundaryCurve));
//                         //}
//                         foreach(var poly of obstaclesToIgnore(station)) {
//                             l.Add(new DebugCurve(100, 5, "green", poly));
//                         }
//                         foreach(var obstacle of obstacleTree.GetAllLeaves()) {
//                             l.Add(new DebugCurve(100, 1, "red", obstacle));
//                         }
//                         l.Add(new DebugCurve(1, "blue", station.BoundaryCurve));
//                         l.Add(new DebugCurve(1, "blue", adj.BoundaryCurve));
//                         l.Add(new DebugCurve(1, "blue", new LineSegment(adj.Position, adj.Neighbors.First().Position)));
//                         l.Add(new DebugCurve(1, "blue", new LineSegment(station.Position, adj.Position)));
//                         LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l);
//                         //end debug visualization
//                         return false;
//                     }
//                 }
//             }
//         }
//         return true;
//     }
//     void ShowStationWithObstaclesToIgnore(Station station, Array < Polyline > allHitItems) {
//         var l = new Array<DebugCurve>();
//         foreach(var poly of allHitItems) {
//             l.Add(new DebugCurve(100, 0.5, "brown", poly));
//         }
//         if (obstaclesToIgnore(station) != null)
//             foreach(var poly of obstaclesToIgnore(station))
//         l.Add(new DebugCurve(100, 1, "red", poly));
//         foreach(var obstacle of obstacleTree.GetAllLeaves())
//         l.Add(new DebugCurve(50, 0.1, "green", obstacle));
//         l.Add(new DebugCurve(0.1, "blue", new Ellipse(1, 1, station.Position)));
//         LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l);
//     }
//     // edge doesn't cross obstacles
//     // NOTE: use method in CdtIntersection insetad!
//     // <
//     bool EdgeIsLegal(Station stationA, Station stationB, Point a, Point b) {
//         var crossings = InteractiveEdgeRouter.IntersectionsOfLineAndRectangleNodeOverPolyline(new LineSegment(a, b), obstacleTree);
//         Set < Polyline > obstaclesToIgnoreForBundle = ObstaclesToIgnoreForBundle(stationA, stationB);
//         if (crossings.Count < 0) {
//             var l = new Array<DebugCurve>();
//             var crossingSet = new Set<ICurve>(crossings.Select(ii => ii.Segment1));
//             l.AddRange(crossingSet.Select(p => new DebugCurve(100, 1, "red", p)));
//             l.AddRange(obstaclesToIgnoreForBundle.Select(p => new DebugCurve(100, 0.5, "green", p)));
//             LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(l);
//         }
//         return crossings.All(intersectionInfo => obstaclesToIgnoreForBundle.Contains((Polyline)intersectionInfo.Segment1));
//     }
// #endif
// }
//# sourceMappingURL=Intersections.js.map