//  The class calculates obstacles under the shape.
//  We assume that the boundaries are not set for the shape children yet
import { Curve, PointLocation } from '../math/geometry';
import { ConvexHull } from '../math/geometry/convexHull';
import { Polyline } from '../math/geometry/polyline';
import { CreateRectangleNodeOnEnumeration, mkRectangleNode } from '../math/geometry/RTree/RectangleNode';
import { CrossRectangleNodes } from '../math/geometry/RTree/RectangleNodeUtils';
import { flatMap } from '../utils/setOperations';
import { InteractiveObstacleCalculator } from './interactiveObstacleCalculator';
import { Shape } from './shape';
import { TightLooseCouple } from './TightLooseCouple';
export class ShapeObstacleCalculator {
    constructor(shape, tightPadding, loosePadding, shapesToTightLooseCouples) {
        this.MainShape = shape;
        this.TightPadding = tightPadding;
        this.LoosePadding = loosePadding;
        this.ShapesToTightLooseCouples = shapesToTightLooseCouples;
    }
    Calculate() {
        if (this.MainShape.Children.length == 0) {
            return;
        }
        this.CreateTightObstacles();
        this.CreateTigthLooseCouples();
        this.FillTheMapOfShapeToTightLooseCouples();
    }
    FillTheMapOfShapeToTightLooseCouples() {
        const childrenShapeHierarchy = CreateRectangleNodeOnEnumeration(this.MainShape.Children.map((s) => mkRectangleNode(s, s.BoundingBox)));
        CrossRectangleNodes(childrenShapeHierarchy, this.coupleHierarchy, this.TryMapShapeToTightLooseCouple.bind(this));
    }
    TryMapShapeToTightLooseCouple(shape, tightLooseCouple) {
        if (ShapeObstacleCalculator.ShapeIsInsideOfPoly(shape, tightLooseCouple.TightPolyline)) {
            this.ShapesToTightLooseCouples.set(shape, tightLooseCouple);
        }
    }
    //  this test is valid in our situation where the tight polylines are disjoint and the shape can cross only one of them
    static ShapeIsInsideOfPoly(shape, tightPolyline) {
        return Curve.PointRelativeToCurveLocation(shape.BoundaryCurve.start, tightPolyline) == PointLocation.Inside;
    }
    CreateTigthLooseCouples() {
        const couples = new Array();
        for (const tightPolyline of this.tightHierarchy.GetAllLeaves()) {
            const distance = InteractiveObstacleCalculator.FindMaxPaddingForTightPolyline(this.tightHierarchy, tightPolyline, this.LoosePadding);
            const loosePoly = InteractiveObstacleCalculator.LoosePolylineWithFewCorners(tightPolyline, distance);
            couples.push(TightLooseCouple.mk(tightPolyline, new Shape(loosePoly), distance));
        }
        this.coupleHierarchy = CreateRectangleNodeOnEnumeration(couples.map((c) => mkRectangleNode(c, c.TightPolyline.boundingBox)));
    }
    CreateTightObstacles() {
        const tightObstacles = new Set(this.MainShape.Children.map(this.InitialTightPolyline.bind(this)));
        const initialNumberOfTightObstacles = tightObstacles.size;
        this.tightHierarchy = InteractiveObstacleCalculator.RemovePossibleOverlapsInTightPolylinesAndCalculateHierarchy(tightObstacles);
        this.OverlapsDetected = initialNumberOfTightObstacles > tightObstacles.size;
    }
    InitialTightPolyline(shape) {
        const poly = InteractiveObstacleCalculator.PaddedPolylineBoundaryOfNode(shape.BoundaryCurve, this.TightPadding);
        const stickingPointsArray = flatMap(this.LoosePolylinesUnderShape(shape), (p) => Array.from(p)).filter((p) => Curve.PointRelativeToCurveLocation(p, poly) == PointLocation.Outside);
        if (stickingPointsArray.length <= 0) {
            return poly;
        }
        const pts = Array.from(poly).concat(stickingPointsArray);
        return Polyline.mkClosedFromPoints(ConvexHull.CalculateConvexHull(pts));
    }
    LoosePolylinesUnderShape(shape) {
        return shape.Children.map((child) => this.ShapesToTightLooseCouples.get(child).LooseShape.BoundaryCurve);
    }
}
//# sourceMappingURL=ShapeObstacleCalculator.js.map