"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.roundPoint = exports.vectorAngle = exports.radBetween = exports.angleBetween = exports.applyVector = exports.NULL_VECTOR = exports.ellipsoidalToCartesian = exports.TWO_PI = exports.DEG = void 0;
exports.randomInRange = randomInRange;
exports.pointDistance = pointDistance;
exports.lineIntersect = lineIntersect;
exports.polarToCartesian = polarToCartesian;
exports.describeArc = describeArc;
exports._describeArc = _describeArc;
exports.describeArrowPolygon = describeArrowPolygon;
exports.describeCircle = describeCircle;
exports.describeRect = describeRect;
exports.eccentricity = eccentricity;
exports.describeEllipseArc = describeEllipseArc;
exports.getRandomXYInArea = getRandomXYInArea;
exports.randomAngle = randomAngle;
exports.angleToRad = angleToRad;
exports.radToAngle = radToAngle;
exports.randomInRadius = randomInRadius;
exports.randomInAngle = randomInAngle;
exports.randomInRect = randomInRect;
exports.randomInPaddedRect = randomInPaddedRect;
exports.randomInCircle = randomInCircle;
exports.pointInCircle = pointInCircle;
exports.pointInRectangle = pointInRectangle;
exports.pointInNode = pointInNode;
exports.unitVector = unitVector;
exports.pointUnitVector = pointUnitVector;
exports.oppositeVector = oppositeVector;
exports.vector = vector;
exports.pointVector = pointVector;
exports.jigglePoint = jigglePoint;
exports.perpendicular = perpendicular;
exports.inRadius = inRadius;
exports.distance = distance;
exports.norm = norm;
exports.normalize = normalize;
exports.mediumPoint = mediumPoint;
exports.randomUnitVector = randomUnitVector;
exports.rotateVector = rotateVector;
exports.addVector = addVector;
exports.addVector3 = addVector3;
exports.addVector4 = addVector4;
exports.subtractVector = subtractVector;
exports.resizeVector = resizeVector;
exports.rectBoundingRadius = rectBoundingRadius;
exports.rectsBoundingRect = rectsBoundingRect;
exports.centerRectsBoundingRect = centerRectsBoundingRect;
exports.centerRectsBoundingLine = centerRectsBoundingLine;
exports.rectangleRegion = rectangleRegion;
exports.isInside = isInside;
exports.surfaceDistance = surfaceDistance;
exports._surfaceDistance = _surfaceDistance;
exports.surfaceDistanceY = surfaceDistanceY;
exports.centerToSurfaceDistance = centerToSurfaceDistance;
exports._centerToSurfaceDistance = _centerToSurfaceDistance;
exports.rectCenter = rectCenter;
exports.centerOfMass = centerOfMass;
exports.catmullRom = catmullRom;
exports.catmullRomSpline = catmullRomSpline;
exports.catmullRomSplineSegment = catmullRomSplineSegment;
const math_1 = require("../../math");
exports.DEG = Math.PI / 180;
exports.TWO_PI = 2 * Math.PI;
function randomInRange(a, b) {
    return a + Math.random() * (b - a);
}
function pointDistance(a, b) {
    return distance(a.x, a.y, b.x, b.y);
}
function lineIntersect(x0, y0, x1, y1, x2, y2, x3, y3) {
    const d = (x1 - x0) * (y3 - y2) - (x3 - x2) * (y1 - y0); // determinant
    if (d === 0) {
        return false;
    }
    else {
        const a = ((y3 - y2) * (x3 - x0) + (x2 - x3) * (y3 - y0)) / d;
        const b = ((y0 - y1) * (x3 - x0) + (x1 - x0) * (y3 - y0)) / d;
        return 0 < a && a < 1 && 0 < b && b < 1;
    }
}
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
    return {
        x: centerX + radius * Math.cos(angleInRadians),
        y: centerY + radius * Math.sin(angleInRadians),
    };
}
function describeArc(x, y, radius, startAngle, endAngle) {
    const start = polarToCartesian(x, y, radius, endAngle);
    const end = polarToCartesian(x, y, radius, startAngle);
    const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
    const d = `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 0 ${end.x} ${end.y}`;
    return d;
}
function _describeArc(x, y, radius, startAngle, endAngle) {
    const start = polarToCartesian(x, y, radius, endAngle);
    const end = polarToCartesian(x, y, radius, startAngle);
    const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
    const d = `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 0 ${end.x} ${end.y}`;
    return d;
}
function describeArrowPolygon(x0, y0, x1, y1, h, s, th, tl) {
    const dx = x1 - x0;
    const dy = y1 - y0;
    const d = Math.sqrt(dx * dx + dy * dy);
    const c = d - th;
    const ux = dx / d;
    const uy = dy / d;
    let alpha;
    if (ux === 0) {
        alpha = ((uy / Math.abs(uy)) * Math.PI) / 2;
    }
    else {
        alpha = Math.atan(uy / ux);
    }
    const px = Math.sin(alpha);
    const py = -Math.cos(alpha);
    const a = (s + h) / 2;
    const apx = a * px;
    const apy = a * py;
    const atl = a + tl;
    const atlpx = atl * px;
    const atlpy = atl * py;
    const cux = c * ux;
    const cuy = c * uy;
    const x0cux = x0 + cux;
    const y0cuy = y0 + cuy;
    const P0 = `${x0 + apx},${y0 + apy}`;
    return `
    ${P0}
    ${x0cux + apx},${y0cuy + apy}
    ${x0cux + atlpx},${y0cuy + atlpy}
    ${x1},${y1}
    ${x0cux - atlpx},${y0cuy - atlpy}
    ${x0cux - apx},${y0cuy - apy}
    ${x0 - apx},${y0 - apy}
    ${P0}
 `;
}
function describeCircle(x, y, r) {
    const d = `M ${x - r} ${y} A ${r} ${r} 0 1 0 ${x - r} ${y - 0.001} z`;
    return d;
}
function describeRect(x, y, width, height) {
    const d = `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
    return d;
}
function eccentricity(rx, ry, angle) {
    // flattening factor
    const ff = 1 - ry / rx;
    const e2 = 2 * ff - ff * ff;
    return e2;
}
const ellipsoidalToCartesian = (cx, cy, rx, ry, angle) => {
    // flattening factor
    const ff = 1 - ry / rx;
    let e2 = 2 * ff;
    e2 -= ff * ff;
    const N = rx / Math.sqrt(1 - e2 * Math.pow(Math.sin(angle), 2));
    const x = cx - N * Math.cos(angle);
    const y = cy - N * Math.sin(angle);
    return { x, y };
};
exports.ellipsoidalToCartesian = ellipsoidalToCartesian;
function describeEllipseArc(x, y, rx, ry, startAngle, endAngle) {
    const start = (0, exports.ellipsoidalToCartesian)(x, y, rx, ry, startAngle);
    const end = (0, exports.ellipsoidalToCartesian)(x, y, rx, ry, endAngle);
    const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
    const d = `M ${start.x} ${start.y} A ${rx} ${ry} 0 ${largeArcFlag} 0 ${end.x} ${end.y}`;
    return d;
}
function getRandomXYInArea(width, height, cX = 0, cY = 0) {
    return {
        x: randomInRange(cX - width / 2, cX + width / 2),
        y: randomInRange(cY - height / 2, cY + height / 2),
    };
}
function randomAngle() {
    return Math.random() * exports.TWO_PI;
}
function angleToRad(angle) {
    return ((angle % 360) * Math.PI) / 180;
}
function radToAngle(rad) {
    return (rad * 180) / Math.PI;
}
function randomInRadius(cX, cY, R) {
    const angle = randomAngle();
    return {
        x: cX + R * Math.cos(angle),
        y: cY + R * Math.sin(angle),
    };
}
function randomInAngle(cX, cY, R, start, end) {
    const angle = (0, math_1.randomIntegerBetween)(start, end);
    const rad = angleToRad(angle);
    return {
        x: cX + R * Math.cos(rad),
        y: cY + R * Math.sin(rad),
    };
}
function randomInRect(x0, y0, x1, y1) {
    return {
        x: x0 + Math.random() * (x1 - x0),
        y: y0 + Math.random() * (y1 - y0),
    };
}
function randomInPaddedRect(x0, y0, x1, y1, offset) {
    return randomInRect(x0 - offset, y0 - offset, x1 + offset, y1 + offset);
}
function randomInCircle(cX, cY, R) {
    const angle = randomAngle();
    return {
        x: cX + Math.random() * R * Math.cos(angle),
        y: cY + Math.random() * R * Math.sin(angle),
    };
}
function pointInCircle(x, y, R, u, padding = 0) {
    return {
        x: x + u.x * (R - padding),
        y: y + u.y * (R - padding),
    };
}
function pointInRectangle(x, y, width, height, u, padding = 0) {
    const region = rectangleRegion(x, y, width, height, u);
    const tan = u.x / u.y;
    const a = Math.atan2(u.y, u.x);
    if (region === 'left' || region === 'right') {
        const sx = Math.sign(u.x);
        return {
            x: x + sx * (width / 2) - padding * Math.cos(a),
            y: y + sx * (width / 2 / tan) - padding * Math.sin(a),
        };
    }
    else {
        const sy = Math.sign(u.y);
        return {
            x: x + sy * ((height / 2) * tan) - padding * Math.cos(a),
            y: y + sy * (height / 2) - padding * Math.sin(a),
        };
    }
}
function pointInNode(node, u, padding = 0) {
    const { shape } = node;
    if (shape === 'circle') {
        return pointInCircle(node.x, node.y, node.r, u, padding);
    }
    else {
        return pointInRectangle(node.x, node.y, node.width, node.height, u, padding);
    }
}
exports.NULL_VECTOR = { x: 0, y: 0 };
function unitVector(x0, y0, x1, y1) {
    const dx = x1 - x0;
    const dy = y1 - y0;
    const d = norm(dx, dy);
    if (d === 0) {
        return randomUnitVector();
    }
    return { x: dx / d, y: dy / d };
}
function pointUnitVector({ x: x0, y: y0 }, { x: x1, y: y1 }) {
    const dx = x1 - x0;
    const dy = y1 - y0;
    const d = norm(dx, dy);
    if (d === 0) {
        return randomUnitVector();
    }
    return { x: dx / d, y: dy / d };
}
function oppositeVector(vector) {
    const { x, y } = vector;
    return {
        x: -x,
        y: -y,
    };
}
const applyVector = (point, u, d) => {
    return {
        x: point.x + u.x * d,
        y: point.y + u.y * d,
    };
};
exports.applyVector = applyVector;
function vector(x0, y0, x1, y1) {
    return { x: x1 - x0, y: y1 - y0 };
}
function pointVector(p0, p1) {
    return vector(p0.x, p0.y, p1.x, p1.y);
}
function jigglePoint(point, intensity = 1) {
    const r = randomUnitVector();
    const jiggled = {
        x: point.x + intensity * r.x,
        y: point.y + intensity * r.y,
    };
    return jiggled;
}
function perpendicular(vector) {
    return { x: -vector.y, y: vector.x };
}
// return the angle between two vectors in degrees
// [0, 360[
// assumes 0 is the origin
const angleBetween = (ax, ay, bx, by) => {
    return radToAngle((0, exports.radBetween)(ax, ay, bx, by));
};
exports.angleBetween = angleBetween;
const radBetween = (ax, ay, bx, by) => {
    // const d = distance(ax, ay, bx, by)
    // if (d === 0) {
    //   return 0
    // }
    let a = Math.atan2(by, bx) - Math.atan2(ay, ax);
    if (a < 0) {
        a += exports.TWO_PI;
    }
    return a;
};
exports.radBetween = radBetween;
function inRadius(cX, cY, R, angle) {
    const rad = angleToRad(angle);
    return {
        x: cX + R * Math.cos(rad),
        y: cY + R * Math.sin(rad),
    };
}
function distance(ax, ay, bx, by) {
    return norm(ax - bx, ay - by);
}
function norm(x, y) {
    return Math.sqrt(x * x + y * y);
}
function normalize(point) {
    const { x, y } = point;
    const d = norm(point.x, point.y);
    return { x: x / d, y: y / d };
}
function mediumPoint(a, b) {
    return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
}
function randomUnitVector() {
    return normalize({ x: 0.5 - Math.random(), y: 0.5 - Math.random() });
}
function rotateVector(a, rad) {
    const cos = Math.cos(rad);
    const sin = Math.sin(rad);
    return { x: a.x * cos - a.y * sin, y: a.x * sin + a.y * cos };
}
function addVector(a, b) {
    return { x: a.x + b.x, y: a.y + b.y };
}
function addVector3(a, b, c) {
    return addVector(addVector(a, b), c);
}
function addVector4(a, b, c, d) {
    return addVector(addVector(a, b), addVector(c, d));
}
function subtractVector(a, b) {
    return { x: a.x - b.x, y: a.y - b.y };
}
function resizeVector(a, k) {
    return { x: a.x * k, y: a.y * k };
}
function rectBoundingRadius(width, height) {
    return Math.sqrt(width * width + height + height) / 2;
}
function rectsBoundingRect(rects) {
    if (rects.length === 0) {
        return {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        };
    }
    let minX = Infinity;
    let minY = Infinity;
    let maxX = -Infinity;
    let maxY = -Infinity;
    for (const rect of rects) {
        minX = Math.min(minX, rect.x);
        minY = Math.min(minY, rect.y);
        maxX = Math.max(maxX, rect.x + rect.width);
        maxY = Math.max(maxY, rect.y + rect.height);
    }
    return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
}
function centerRectsBoundingRect(rects) {
    if (rects.length === 0) {
        return {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        };
    }
    let minX = Infinity;
    let minY = Infinity;
    let maxX = -Infinity;
    let maxY = -Infinity;
    for (const rect of rects) {
        minX = Math.min(minX, rect.x - rect.width / 2);
        minY = Math.min(minY, rect.y - rect.height / 2);
        maxX = Math.max(maxX, rect.x + rect.width / 2);
        maxY = Math.max(maxY, rect.y + rect.height / 2);
    }
    const width = maxX - minX;
    const height = maxY - minY;
    return { x: minX + width / 2, y: minY + height / 2, width, height };
}
function centerRectsBoundingLine(rects) {
    if (rects.length === 0) {
        return {
            x0: 0,
            y0: 0,
            x1: 0,
            y1: 0,
        };
    }
    let x0 = Infinity;
    let y0 = Infinity;
    let x1 = -Infinity;
    let y1 = -Infinity;
    for (const rect of rects) {
        x0 = Math.min(x0, rect.x - rect.width / 2);
        y0 = Math.min(y0, rect.y - rect.height / 2);
        x1 = Math.max(x1, rect.x + rect.width / 2);
        y1 = Math.max(y1, rect.y + rect.height / 2);
    }
    return { x0, y0, x1, y1 };
}
function rectangleRegion(x, y, w, h, u) {
    const { x: ux, y: uy } = u;
    const { x: ax, y: ay } = unitVector(x, y, x - w / 2, y - h / 2);
    const { x: bx, y: by } = unitVector(x, y, x + w / 2, y - h / 2);
    const alpha = (0, exports.radBetween)(ax, ay, bx, by);
    const beta = (0, exports.radBetween)(ux, uy, bx, by);
    if (beta <= alpha) {
        return 'top';
    }
    else if (beta <= Math.PI) {
        return 'left';
    }
    else if (beta <= Math.PI + alpha) {
        return 'bottom';
    }
    else {
        return 'right';
    }
}
function isInside(a, b, offset = 0) {
    if (a.shape === 'circle' && b.shape === 'circle') {
        return distance(a.x, a.y, b.x, b.y) <= b.r - a.r + offset;
    }
    else if (a.shape === 'circle' && b.shape === 'rect') {
        return (a.x - a.r >= b.x - b.width / 2 - offset &&
            a.x + a.r <= b.x + b.width / 2 + offset &&
            a.y - a.r >= b.y - b.height / 2 - offset &&
            a.y + a.r <= b.y + b.height / 2 + offset);
    }
    else if (a.shape === 'rect' && b.shape === 'circle') {
        // https://stackoverflow.com/questions/14097290/check-if-circle-contains-rectangle
        const dx = Math.max(b.x - a.x + a.width / 2, a.x + a.width / 2 - b.x);
        const dy = Math.max(b.y - a.y + a.height / 2, a.y + a.height / 2 - b.y);
        return b.r >= Math.sqrt(dx * dx + dy * dy) - offset;
    }
    else {
        return (a.x - a.width / 2 >= b.x - b.width / 2 - offset &&
            a.x + a.width / 2 <= b.x + b.width / 2 + offset &&
            a.y - a.height / 2 >= b.y - b.height / 2 - offset &&
            a.y + a.height / 2 <= b.y + b.height / 2 + offset);
    }
}
function surfaceDistance(a, b) {
    return _surfaceDistance(a.shape, a.x, a.y, a.r, a.width, a.height, b.shape, b.x, b.y, b.r, b.width, b.height);
}
function _surfaceDistance(a_shape, a_x, a_y, a_r, a_width, a_height, b_shape, b_x, b_y, b_r, b_width, b_height) {
    const d = distance(a_x, a_y, b_x, b_y);
    const u = unitVector(a_x, a_y, b_x, b_y);
    const a_d = _centerToSurfaceDistance(a_shape, a_x, a_y, a_r, a_width, a_height, u);
    const b_d = _centerToSurfaceDistance(b_shape, b_x, b_y, b_r, b_width, b_height, u);
    const d_sum = b_d + a_d;
    const l = d - d_sum;
    return {
        d,
        l,
        u,
    };
}
function surfaceDistanceY(a, b) {
    const a_y = a.y;
    const b_y = b.y;
    const d = Math.abs(a_y - b_y);
    const u = Math.sign(a_y - b_y);
    const a_d = a.height / 2;
    const b_d = b.height / 2;
    let l;
    const ds = b_d + a_d;
    const dd = Math.abs(a_d - b_d);
    if (d <= ds && d >= dd - 1) {
        l = 0;
    }
    else if (d < dd) {
        l = d - dd;
    }
    l = d - ds;
    return {
        d,
        l,
        u,
    };
}
function centerToSurfaceDistance(node, u) {
    const { shape, x, y, r, width, height } = node;
    return _centerToSurfaceDistance(shape, x, y, r, width, height, u);
}
function _centerToSurfaceDistance(shape, x, y, r, width, height, u) {
    if (shape === 'circle') {
        return r;
    }
    else {
        const tan = u.x / u.y;
        const region = rectangleRegion(x, y, width, height, u);
        if (region === 'left' || region === 'right') {
            return norm(width / 2, width / 2 / tan);
        }
        else {
            return norm((height / 2) * tan, height / 2);
        }
    }
}
function rectCenter(rect) {
    return {
        x: rect.x + rect.width / 2,
        y: rect.y + rect.height / 2,
    };
}
function centerOfMass(points) {
    let sum_x = 0;
    let sum_y = 0;
    const n = points.length;
    for (let i = 0; i < points.length; i++) {
        const p = points[i];
        sum_x += p.x;
        sum_y += p.y;
    }
    const x = sum_x / n;
    const y = sum_y / n;
    return { x, y };
}
const vectorAngle = (p0, p1) => {
    let a = Math.atan2(p1.y - p0.y, p1.x - p0.x);
    if (a < 0) {
        a += exports.TWO_PI;
    }
    return a / exports.DEG;
};
exports.vectorAngle = vectorAngle;
const roundPoint = (position) => {
    return {
        x: Math.round(position.x),
        y: Math.round(position.y),
    };
};
exports.roundPoint = roundPoint;
function catmullRom(p0, p1, p2, p3, t) {
    const tensionFactor = 0.5;
    const startingPointWeight = 2;
    const linearTerm1 = -1;
    const linearTerm2 = 1;
    const quadraticTerm1 = 2;
    const quadraticTerm2 = -5;
    const quadraticTerm3 = 4;
    const quadraticTerm4 = -1;
    const cubicTerm1 = -1;
    const cubicTerm2 = 3;
    const cubicTerm3 = -3;
    const cubicTerm4 = 1;
    const t2 = t * t;
    const t3 = t2 * t;
    return (tensionFactor *
        (startingPointWeight * p1 +
            (linearTerm1 * p0 + linearTerm2 * p2) * t +
            (quadraticTerm1 * p0 +
                quadraticTerm2 * p1 +
                quadraticTerm3 * p2 +
                quadraticTerm4 * p3) *
                t2 +
            (cubicTerm1 * p0 + cubicTerm2 * p1 + cubicTerm3 * p2 + cubicTerm4 * p3) *
                t3));
}
function catmullRomSpline(points) {
    if (points.length < 4)
        return;
    const spline = [];
    spline.push([points[0].x, points[0].y]);
    for (let i = 0; i < points.length - 3; i++) {
        const fourPoints = points.slice(i, i + 4);
        const segment = catmullRomSplineSegment(fourPoints);
        for (let i = 0; i < segment.length; i++) {
            spline.push(segment[i]);
        }
    }
    return spline;
}
function catmullRomSplineSegment(lastFourPoints, step = 0.1) {
    if (lastFourPoints.length < 4) {
        return [];
    }
    const spline = [];
    spline.push([lastFourPoints[0].x, lastFourPoints[0].y]);
    const [p0, p1, p2, p3] = lastFourPoints;
    for (let t = 0; t <= 1; t += step) {
        const x = catmullRom(p0.x, p1.x, p2.x, p3.x, t);
        const y = catmullRom(p0.y, p1.y, p2.y, p3.y, t);
        spline.push([x, y]);
    }
    return spline;
}
//# sourceMappingURL=index.js.map