"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Point2 = exports.Offset2 = void 0;
const fn_1 = require("../fn");
class Offset2 {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
    clone() {
        return new Offset2(this.x, this.y);
    }
    dot(z) {
        return this.x * z.x + this.y * z.y;
    }
    add(b) {
        return new Offset2(this.x + b.x, this.y + b.y);
    }
    addScale(s, b) {
        return new Offset2(this.x + s * b.x, this.y + s * b.y);
    }
    minus(b) {
        return new Offset2(this.x - b.x, this.y - b.y);
    }
    mix(b, t) {
        return new Offset2(this.x + (b.x - this.x) * t, this.y + (b.y - this.y) * t);
    }
    angle() {
        return Math.atan2(this.y, this.x);
    }
    mag() {
        return Math.hypot(this.x, this.y);
    }
    isAlmostZero(ep = fn_1.EPSILON) {
        return this.x * this.x + this.y * this.y < ep * ep;
    }
    scale(t) {
        return new Offset2(this.x * t, this.y * t);
    }
    scaleXY(tx, ty) {
        return new Offset2(this.x * tx, this.y * ty);
    }
    toLength(d) {
        const h = Math.hypot(this.x, this.y);
        return new Offset2((d * this.x) / h, (d * this.y) / h);
    }
    static scaleFrom(s, p) {
        return new Offset2(s * p.x, s * p.y);
    }
    static from(p) {
        return new Offset2(p.x, p.y);
    }
    static differenceFrom(a, b) {
        return new Offset2(a.x - b.x, a.y - b.y);
    }
    static dot(a, b) {
        return a.x * b.x + a.y * b.y;
    }
    static cross(a, b) {
        return a.x * b.y - a.y * b.x;
    }
    static addScale(a, s, b) {
        return new Offset2(a.x + s * b.x, a.y + s * b.y);
    }
}
exports.Offset2 = Offset2;
class Point2 {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
    clone() {
        return new Point2(this.x, this.y);
    }
    mix(b, t) {
        return new Point2(this.x + (b.x - this.x) * t, this.y + (b.y - this.y) * t);
    }
    add(b) {
        return new Point2(this.x + b.x, this.y + b.y);
    }
    addScale(s, b) {
        return new Point2(this.x + s * b.x, this.y + s * b.y);
    }
    minus(b) {
        return new Offset2(this.x - b.x, this.y - b.y);
    }
    scaleAround(z, t) {
        return z.add(this.minus(z).scale(t));
    }
    scaleAroundXY(z, tx, ty) {
        return z.add(this.minus(z).scaleXY(tx, ty));
    }
    isClose(b, tolerance) {
        return Point2.squareDist(this, b) <= tolerance * tolerance;
    }
    static intersect(a, b, c, d, fInfinite = false) {
        const p1x = a.x, p1y = a.y, v1x = b.x - a.x, v1y = b.y - a.y, p2x = c.x, p2y = c.y, v2x = d.x - c.x, v2y = d.y - c.y;
        const cross = v1x * v2y - v1y * v2x;
        if (!(0, fn_1.numberClose)(cross, 0, fn_1.MACHINE_EPSILON)) {
            let dx = p1x - p2x, dy = p1y - p2y, u1 = (v2x * dy - v2y * dx) / cross, u2 = (v1x * dy - v1y * dx) / cross, uMin = -fn_1.EPSILON, uMax = 1 + fn_1.EPSILON;
            if (fInfinite || (uMin < u1 && u1 < uMax && uMin < u2 && u2 < uMax)) {
                if (!fInfinite)
                    u1 = u1 <= 0 ? 0 : u1 >= 1 ? 1 : u1;
                return new Point2(p1x + u1 * v1x, p1y + u1 * v1y);
            }
        }
        return null;
    }
    static rayIntersection(p1, _d1, _d2, p2) {
        const d1 = Offset2.differenceFrom(_d1, p1);
        const d2 = Offset2.differenceFrom(_d2, p2);
        const det = d2.x * d1.y - d2.y * d1.x;
        const numU = (p2.y - p1.y) * d2.x - (p2.x - p1.x) * d2.y;
        const numV = (p2.y - p1.y) * d1.x - (p2.x - p1.x) * d1.y;
        if (Math.abs(det) < 1e-6) {
            if (Math.abs(numU) < 1e-12 &&
                Math.abs(numV) < 1e-12 &&
                numU * det <= 0 &&
                numV * det <= 0) {
                return new Point2((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
            }
            else {
                return null;
            }
        }
        const u = numU / det;
        const v = numV / det;
        if (u <= 0 || v <= 0)
            return null;
        return new Point2(p1.x + d1.x * u, p1.y + d1.y * u);
    }
    static project(a, b, p) {
        const scalar = Point2.scalarProject(a, b, p);
        return Point2.from(a).mix(Point2.from(b), scalar);
    }
    static scalarProject(a, b, p) {
        const apx = p.x - a.x, apy = p.y - a.y;
        const abx = b.x - a.x, aby = b.y - a.y;
        return (apx * abx + apy * aby) / (abx * abx + aby * aby);
    }
    static cosAngle(a, b, p) {
        const vp = Offset2.differenceFrom(p, a);
        const vb = Offset2.differenceFrom(b, a);
        return Math.min(1, Math.max(-1, vp.dot(vb) / (vp.mag() * vb.mag())));
    }
    static squareDist(a, b) {
        return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
    }
    static areClose(a, b, t) {
        return Point2.squareDist(a, b) < t * t;
    }
    static dist(a, b) {
        return Math.hypot(a.x - b.x, a.y - b.y);
    }
    static signedPointLineDist(a, b, p) {
        return signedDistanceImpl(a.x, a.y, b.x, b.y, p.x, p.y);
    }
    static pointLineDist(a, b, p) {
        return getDistanceImpl(a.x, a.y, b.x, b.y, p.x, p.y);
    }
    static from(p) {
        return new Point2(p.x, p.y);
    }
    static addScale(a, s, b) {
        return new Point2(a.x + s * b.x, a.y + s * b.y);
    }
}
exports.Point2 = Point2;
function signedDistanceImpl(px, py, vx, vy, x, y) {
    vx -= px;
    vy -= py;
    if ((0, fn_1.numberClose)(vx, 0)) {
        return vy >= 0 ? px - x : x - px;
    }
    else if ((0, fn_1.numberClose)(vy, 0)) {
        return vx >= 0 ? y - py : py - y;
    }
    else {
        return (vx * (y - py) - vy * (x - px)) / Math.sqrt(vx * vx + vy * vy);
    }
}
function getDistanceImpl(px, py, vx, vy, x, y) {
    return Math.abs(signedDistanceImpl(px, py, vx, vy, x, y));
}
