import { CompassVector } from '../../math/geometry/compassVector';
import { Curve } from '../../math/geometry/curve';
import { GeomConstants } from '../../math/geometry/geomConstants';
import { Point, TriangleOrientation } from '../../math/geometry/point';
import { Polyline } from '../../math/geometry/polyline';
// import {Assert} from '../../utils/assert'
import { substractSets } from '../../utils/setOperations';
import { InteractiveEdgeRouter } from '../InteractiveEdgeRouter';
import { InteractiveObstacleCalculator } from '../interactiveObstacleCalculator';
import { LowObstacleSide, HighObstacleSide } from './BasicObstacleSide';
export class Obstacle {
    constructor(shape, makeRect, padding) {
        if (shape == null) {
            return;
        }
        if (makeRect) {
            const paddedBox = shape.BoundingBox.clone();
            paddedBox.pad(padding);
            this.PaddedPolyline = Curve.polyFromBox(paddedBox);
        }
        else {
            this.PaddedPolyline = InteractiveObstacleCalculator.PaddedPolylineBoundaryOfNode(shape.BoundaryCurve, padding);
        }
        Obstacle.RoundVerticesAndSimplify(this.PaddedPolyline);
        this.IsRectangle = this.IsPolylineRectangle();
        this.InputShape = shape;
        this.Ports = new Set(this.InputShape.Ports);
    }
    get PaddedPolyline() {
        return this._PaddedPolyline;
    }
    set PaddedPolyline(value) {
        this._PaddedPolyline = value;
    }
    GetPortChanges(t) {
        t.addedPorts = substractSets(this.InputShape.Ports, this.Ports);
        t.removedPorts = substractSets(this.Ports, this.InputShape.Ports);
        if (0 == t.addedPorts.size && 0 == t.removedPorts.size) {
            return false;
        }
        this.Ports = new Set(this.InputShape.Ports);
        return true;
    }
    get IsInConvexHull() {
        return this.ConvexHull != null;
    }
    get IsGroup() {
        return this.InputShape != null && this.InputShape.IsGroup;
    }
    get VisibilityBoundingBox() {
        return this.VisibilityPolyline.boundingBox;
    }
    get VisibilityPolyline() {
        return this.ConvexHull != null ? this.ConvexHull.Polyline : this.PaddedPolyline;
    }
    static CreateSentinel(a, b, scanDir, scanlineOrdinal) {
        const sentinel = Obstacle.mk(a, b, scanlineOrdinal);
        sentinel.CreateInitialSides(sentinel.PaddedPolyline.startPoint, scanDir);
        return sentinel;
    }
    CreateInitialSides(startPoint, scanDir) {
        /*Assert.assert(
          this.ActiveLowSide == null && this.ActiveHighSide == null,
          'Cannot call SetInitialSides when sides are already set',
        )*/
        this.ActiveLowSide = new LowObstacleSide(this, startPoint, scanDir);
        this.ActiveHighSide = new HighObstacleSide(this, startPoint, scanDir);
        if (scanDir.IsFlatS(this.ActiveHighSide)) {
            //  No flat sides in the scanline; we'll do lookahead processing in the scanline to handle overlaps
            //  with existing segments, and normal neighbor handling will take care of collinear OpenVertexEvents.
            this.ActiveHighSide = new HighObstacleSide(this, this.ActiveHighSide.EndVertex, scanDir);
        }
    }
    static mk(a, b, scanlineOrdinal) {
        const obs = new Obstacle(null, false, 0);
        obs.PaddedPolyline = Polyline.mkClosedFromPoints([GeomConstants.RoundPoint(a), GeomConstants.RoundPoint(b)]);
        obs.Ordinal = scanlineOrdinal;
        return obs;
    }
    IsPolylineRectangle() {
        if (this.PaddedPolyline.count != 4) {
            return false;
        }
        let ppt = this.PaddedPolyline.startPoint;
        let nextPpt = ppt.nextOnPolyline;
        let dir = CompassVector.VectorDirectionPP(ppt.point, nextPpt.point);
        if (!CompassVector.IsPureDirection(dir)) {
            return false;
        }
        do {
            ppt = nextPpt;
            nextPpt = ppt.nextOnPolyline;
            const nextDir = CompassVector.DirectionFromPointToPoint(ppt.point, nextPpt.point);
            //  We know the polyline is clockwise.
            if (nextDir != CompassVector.RotateRight(dir)) {
                return false;
            }
            dir = nextDir;
        } while (ppt != this.PaddedPolyline.startPoint);
        return true;
    }
    static RoundVerticesAndSimplify(polyline) {
        /*Assert.assert(polyline.isClockwise(), 'Polyline is not clockwise')*/
        /*Assert.assert(polyline.closed)*/
        //  Following creation of the padded border, round off the vertices for consistency
        //  in later operations (intersections and event ordering).
        let ppt = polyline.startPoint;
        do {
            ppt.point = GeomConstants.RoundPoint(ppt.point);
            ppt = ppt.nextOnPolyline;
        } while (ppt != polyline.startPoint);
        Obstacle.RemoveCloseAndCollinearVerticesInPlace(polyline);
        //  We've modified the points so the BoundingBox may have changed; force it to be recalculated.
        polyline.setInitIsRequired();
        //  Verify that the polyline is still clockwise.
        /*Assert.assert(
          polyline.isClockwise(),
          'Polyline is not clockwise after RoundVertices',
        )*/
    }
    // A single convex hull is shared by all obstacles contained by it and we only want one occurrence of that
    // convex hull's polyline in the visibility graph generation.
    get IsPrimaryObstacle() {
        return this.ConvexHull == null || this == this.ConvexHull.PrimaryObstacle;
    }
    static RemoveCloseAndCollinearVerticesInPlace(polyline) {
        const epsilon = GeomConstants.intersectionEpsilon * 10;
        for (let pp = polyline.startPoint.next; pp != null; pp = pp.next) {
            if (Point.close(pp.prev.point, pp.point, epsilon)) {
                if (pp.next == null) {
                    polyline.RemoveEndPoint();
                }
                else {
                    pp.prev.next = pp.next;
                    pp.next.prev = pp.prev;
                }
            }
        }
        if (Point.close(polyline.start, polyline.end, epsilon)) {
            polyline.RemoveStartPoint();
        }
        InteractiveEdgeRouter.RemoveCollinearVertices(polyline);
        if (polyline.endPoint.prev != null &&
            polyline.endPoint.prev != polyline.startPoint &&
            Point.getTriangleOrientation(polyline.endPoint.prev.point, polyline.end, polyline.start) == TriangleOrientation.Collinear) {
            polyline.RemoveEndPoint();
        }
        if (polyline.startPoint.next != null &&
            polyline.endPoint.prev != polyline.startPoint &&
            Point.getTriangleOrientation(polyline.end, polyline.start, polyline.startPoint.next.point) == TriangleOrientation.Collinear) {
            polyline.RemoveStartPoint();
        }
        polyline.setInitIsRequired();
        return polyline;
    }
    get isOverlapped() {
        return this.clump != undefined && this.clump.length > 0;
    }
    get IsSentinel() {
        return this.InputShape == null;
    }
    IsInSameClump(other) {
        return this.isOverlapped && this.clump == other.clump;
    }
    Close() {
        this.ActiveLowSide = null;
        this.ActiveHighSide = null;
    }
    SetConvexHull(hull) {
        //  This obstacle may have been in a rectangular obstacle or clump that was now found to overlap with a non-rectangular obstacle.
        this.clump = null;
        this.IsRectangle = false;
        this.ConvexHull = hull;
        this.looseVisibilityPolyline = null;
    }
    static CreateLoosePolyline(polyline) {
        const loosePolyline = InteractiveObstacleCalculator.CreatePaddedPolyline(polyline, GeomConstants.intersectionEpsilon * 10);
        Obstacle.RoundVerticesAndSimplify(loosePolyline);
        return loosePolyline;
    }
    get IsTransparentAncestor() {
        return this.InputShape == null ? false : this.InputShape.IsTransparent;
    }
    set IsTransparentAncestor(value) {
        this.InputShape.IsTransparent = value;
    }
}
Obstacle.FirstSentinelOrdinal = 1;
Obstacle.FirstNonSentinelOrdinal = 10;
//# sourceMappingURL=obstacle.js.map