Source: utils/geometry.js

"use strict"

/**
 * ...
 * @namespace utils.geometry
 */

/**
 * Finds the central point (avg.) between the given points
 * @param {[]} points
 * @memberof utils.geometry.points
 * @returns central point (avg.) between the given points
 */
const ptCentroid = (points, mode = '3d') => {
    const min = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]
    const max = [Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER]

    points.forEach(pt => {
        min[0] = Math.min(min[0], pt[0])
        min[1] = Math.min(min[1], pt[1])

        max[0] = Math.max(max[0], pt[0])
        max[1] = Math.max(max[1], pt[1])

        if (mode === '3d') {
            min[2] = Math.min(min[2], pt[2])
            max[2] = Math.max(max[2], pt[2])
        }
    })

    let output = [
        (max[0] + min[0]) / 2,
        (max[1] + min[1]) / 2,
    ]
    if (mode === '3d') {
        output = [...output, (max[2] + min[2]) / 2]
    }
    return output;
}

const geometryUtils = ({ lib, swLib }) => {
    const { maths } = swLib.utils;

    return {
        /**
         * Gets triangular points in area
         * @memberof utils.geometry
         * @param {*} x 
         * @param {*} y 
         * @param {*} radius 
         * @returns ...
         */
        getTriangularPtsInArea: (x, y, radius, centrePoints = true) => {
            const diam = radius * 2;
            const allPoints = [];

            const allYCoords = [];
            let yCoordCtr = 0;
            do {
                allYCoords.push(yCoordCtr);
                yCoordCtr = diam * 0.86603 + yCoordCtr;
            } while (yCoordCtr <= y);

            let yIdxCtr = 0;
            do {
                let xCtr = 0;
                do {
                    if (maths.isEven(yIdxCtr)) {
                        allPoints.push({ x: xCtr, y: allYCoords[yIdxCtr] });
                    } else {
                        allPoints.push({ x: radius + xCtr, y: allYCoords[yIdxCtr] });
                    }
                    xCtr = xCtr + diam;
                } while (xCtr <= x);
                yIdxCtr = yIdxCtr + 1;
            } while (yIdxCtr < allYCoords.length);

            if (!centrePoints) {
                return allPoints
            }

            const simplePts = allPoints.map(pt => [pt.x, pt.y])
            const pointCentroid = ptCentroid(simplePts, '2d');

            return allPoints.map(pt => {
                return {
                    x: pt.x - pointCentroid[0],
                    y: pt.y - pointCentroid[1],
                }
            });
        },
        /**
         * Gets square points in area
         * @memberof utils.geometry
         * @param {*} x 
         * @param {*} y 
         * @param {*} radius 
         * @returns ...
         */
        getSquarePtsInArea: (x, y, radius, centrePoints = true) => {
            const diam = radius * 2;
            const allXCoords = [];
            let xCtr = 0;
            do {
                allXCoords.push(xCtr);
                xCtr = xCtr + diam;
            } while (xCtr <= x);

            const allYCoords = [];
            let yCtr = 0;
            do {
                allYCoords.push(yCtr);
                yCtr = yCtr + diam;
            } while (yCtr <= y);

            const allPoints = maths.arrayCartesianProduct(allXCoords, allYCoords);
            const outPts = allPoints.map(pt => { return { x: pt[0], y: pt[1] } });

            if (!centrePoints) {
                return outPts
            }

            const simplePts = outPts.map(pt => [pt.x, pt.y])
            const pointCentroid = ptCentroid(simplePts, '2d');

            return outPts.map(pt => {
                return {
                    x: pt.x - pointCentroid[0],
                    y: pt.y - pointCentroid[1],
                }
            });
        },
        /**
         * Functions related to sets of Cartesian points
         * @memberof utils.geometry
         * @namespace points
         */
        points: {
            centroid: ptCentroid,
        },
        /**
         * Functions related to regular polygons
         * @memberof utils.geometry
         * @namespace regPoly
         */
        regPoly: {
            /**
             * ...
             * @memberof utils.geometry.regPoly
             * @returns ...
             */
            sideLengthFromApothem: (apothem, numSides) => {
                return apothem * 2 * Math.tan(Math.PI / numSides);
            },
            /**
             * ...
             * @memberof utils.geometry.regPoly
             * @returns ...
             */
            sideLengthFromCircumRadius: (circumradius, numSides) => {
                return circumradius * 2 * Math.sin(Math.PI / numSides);
            },
            /**
             * ...
             * @memberof utils.geometry.regPoly
             * @returns ...
             */
            apothemFromCircumradius: (circumradius, numSides) => {
                return circumradius * Math.cos(Math.PI / numSides)
            },
            /**
             * ...
             * @memberof utils.geometry.regPoly
             * @returns ...
             */
            apothemFromSideLength: (sideLength, numSides) => {
                return sideLength / 2 * Math.tan(Math.PI / numSides)
            },
            /**
             * ...
             * @memberof utils.geometry.regPoly
             * @returns ...
             */
            circumradiusFromApothem: (apothem, numSides) => {
                return apothem / Math.cos(Math.PI / numSides);
            },
            /**
             * ...
             * @memberof utils.geometry.regPoly
             * @returns ...
             */
            circumradiusFromSideLength: (sideLength, numSides) => {
                return sideLength / 2 * Math.sin(Math.PI / numSides)
            },
            /**
             * ...
             * @memberof utils.geometry.regPoly
             * @returns ...
             */
            interiorAngle: (numSides) => {
                return 2 * Math.PI / numSides;
            },
        }
    }
}

module.exports = { init: geometryUtils };