/*
Following "Sweep-line algorithm for constrained Delaunay triangulation", by Domiter and Zalik
*/
//triangulates the space between point, line segment and polygons of the Delaunay fashion
import { GeomConstants } from '../../math/geometry/geomConstants';
import { Point } from '../../math/geometry/point';
import { Rectangle } from '../../math/geometry/rectangle';
// import {Assert} from '../../utils/assert'
import { PointMap } from '../../utils/PointMap';
import { Algorithm } from './../../utils/algorithm';
import { CdtEdge } from './CdtEdge';
import { CdtSite } from './CdtSite';
import { CdtSweeper } from './CdtSweeper';
import { CreateRectangleNodeOnEnumeration, mkRectangleNode } from '../../math/geometry/RTree/RectangleNode';
export class Cdt extends Algorithm {
    // constructor
    constructor(isolatedSites, obstacles, isolatedSegments) {
        super(null);
        this.isolatedSites = [];
        this.obstacles = [];
        this.PointsToSites = new PointMap();
        this.cdtTree = null;
        this.isolatedSites = isolatedSites;
        this.obstacles = obstacles;
        this.isolatedSegments = isolatedSegments;
    }
    //  constructor
    static constructor_(isolatedSitesWithObj) {
        const r = new Cdt(null, null, null);
        r.isolatedSitesWithObject = isolatedSitesWithObj;
        return r;
    }
    FillAllInputSites() {
        // for now suppose that the data is correct: no isolatedSites coincide with obstacles or isolatedSegments, obstacles are mutually disjoint, etc
        if (this.isolatedSitesWithObject != null) {
            for (const tuple of this.isolatedSitesWithObject) {
                this.AddSite(tuple[0], tuple[1]);
            }
        }
        if (this.isolatedSites != null) {
            for (const isolatedSite of this.isolatedSites) {
                this.AddSite(isolatedSite, null);
            }
        }
        if (this.obstacles != null) {
            for (const poly of this.obstacles) {
                this.AddPolylineToAllInputSites(poly);
            }
        }
        if (this.isolatedSegments != null) {
            for (const isolatedSegment of this.isolatedSegments) {
                this.AddConstrainedEdge(isolatedSegment.A, isolatedSegment.B, null);
            }
        }
        this.AddP1AndP2();
        this.allInputSites = Array.from(this.PointsToSites.values());
    }
    AddSite(point, relatedObject) {
        let site;
        if ((site = this.PointsToSites.get(point))) {
            site.Owner = relatedObject;
            // set the owner anyway
        }
        else {
            site = CdtSite.mkSO(point, relatedObject);
            this.PointsToSites.set(point, site);
        }
        return site;
    }
    AddP1AndP2() {
        const box = Rectangle.mkEmpty();
        for (const site of this.PointsToSites.keys()) {
            box.add(site);
        }
        const delx = Math.max(box.width / 3, 1);
        const dely = Math.max(box.height / 3, 1);
        this.P1 = new CdtSite(box.leftBottom.add(new Point(-delx, -dely)));
        this.P2 = new CdtSite(box.rightBottom.add(new Point(delx, -dely)));
    }
    AddPolylineToAllInputSites(poly) {
        for (let pp = poly.startPoint; pp.next != null; pp = pp.next) {
            this.AddConstrainedEdge(pp.point, pp.next.point, poly);
        }
        if (poly.closed) {
            this.AddConstrainedEdge(poly.endPoint.point, poly.startPoint.point, poly);
        }
    }
    AddConstrainedEdge(a, b, poly) {
        const ab = Cdt.AbovePP(a, b);
        /*Assert.assert(ab != 0)*/
        let upperPoint;
        let lowerPoint;
        if (ab > 0) {
            // a is above b
            upperPoint = this.AddSite(a, poly);
            lowerPoint = this.AddSite(b, poly);
        }
        else {
            /*Assert.assert(ab < 0)*/
            upperPoint = this.AddSite(b, poly);
            lowerPoint = this.AddSite(a, poly);
        }
        const edge = Cdt.CreateEdgeOnOrderedCouple(upperPoint, lowerPoint);
        edge.Constrained = true;
        /*Assert.assert(this.EdgeIsCorrect(edge))*/
    }
    static GetOrCreateEdge(a, b) {
        if (Cdt.AboveCC(a, b) == 1) {
            const e = a.EdgeBetweenUpperSiteAndLowerSite(b);
            if (e != null) {
                return e;
            }
            return Cdt.CreateEdgeOnOrderedCouple(a, b);
        }
        else {
            const e = b.EdgeBetweenUpperSiteAndLowerSite(a);
            if (e != null) {
                return e;
            }
            return Cdt.CreateEdgeOnOrderedCouple(b, a);
        }
    }
    static CreateEdgeOnOrderedCouple(upperPoint, lowerPoint) {
        /*Assert.assert(Cdt.AboveCC(upperPoint, lowerPoint) == 1)*/
        return new CdtEdge(upperPoint, lowerPoint);
    }
    GetTriangles() {
        return this.sweeper.triangles;
    }
    //  Executes the actual algorithm.
    run() {
        this.Initialization();
        this.SweepAndFinalize();
    }
    SweepAndFinalize() {
        this.sweeper = new CdtSweeper(this.allInputSites, this.P1, this.P2, Cdt.GetOrCreateEdge);
        this.sweeper.run();
    }
    Initialization() {
        this.FillAllInputSites();
        this.allInputSites.sort(Cdt.OnComparison);
    }
    static OnComparison(a, b) {
        return Cdt.AboveCC(a, b);
    }
    //  compare first y then -x coordinates
    static AbovePP(a, b) {
        let del = a.y - b.y;
        if (del > 0) {
            return 1;
        }
        if (del < 0) {
            return -1;
        }
        del = a.x - b.x;
        // for a horizontal edge return the point with the smaller X
        return del > 0 ? -1 : del < 0 ? 1 : 0;
    }
    //  compare first y then -x coordinates
    static AboveCC(a, b) {
        return Cdt.AbovePP(a.point, b.point);
    }
    RestoreEdgeCapacities() {
        for (const site of this.allInputSites) {
            for (const e of site.Edges) {
                if (!e.Constrained) {
                    e.ResidualCapacity = e.Capacity;
                }
            }
        }
    }
    SetInEdges() {
        for (const site of this.PointsToSites.values()) {
            for (const e of site.Edges) {
                const oSite = e.lowerSite;
                /*Assert.assert(oSite != site)*/
                oSite.AddInEdge(e);
            }
        }
    }
    FindSite(point) {
        return this.PointsToSites.get(point);
    }
    static PointIsInsideOfTriangle(point, t) {
        for (let i = 0; i < 3; i++) {
            const a = t.Sites.getItem(i).point;
            const b = t.Sites.getItem(i + 1).point;
            if (Point.signedDoubledTriangleArea(point, a, b) < GeomConstants.distanceEpsilon * -1) {
                return false;
            }
        }
        return true;
    }
    GetCdtTree() {
        if (this.cdtTree == null) {
            this.cdtTree = CreateRectangleNodeOnEnumeration(Array.from(this.GetTriangles().values()).map((t) => mkRectangleNode(t, t.BoundingBox())));
        }
        return this.cdtTree;
    }
    EdgeIsCorrect(edge) {
        const us = edge.upperSite;
        let edgeIsThere = false;
        for (const e of us.Edges) {
            if (e == edge) {
                edgeIsThere = true;
                break;
            }
        }
        if (!edgeIsThere) {
            return false;
        }
        const usShouldBe = this.PointsToSites.get(us.point);
        return usShouldBe == us;
    }
}
//# sourceMappingURL=Cdt.js.map