game_vector_triangle.js

/**
 * @module Triangle
 * @fileoverview Contains Triangle class.
 */

/**
 * @class
 * Triangle used for collision checks.
 */
class Triangle {
    /**
     * Create a new triangle.
     * @param {Vec2} a First point
     * @param {Vec2} b Second point
     * @param {Vec2} c Third point
     * @constructor
     */
    constructor(a, b, c) {
        this.a = a.copy();
        this.b = b.copy();
        this.c = c.copy();
    }

    /**
     * Check if this triangle is overlapping with another.
     * @param {Triangle} other Triangle to check overlap with
     * @returns {Object} Data containing result
     */
    check(other) {
        const a1 = this.a.copy();
        const b1 = this.b.copy();
        const c1 = this.c.copy();

        const a2 = other.a.copy();
        const b2 = other.b.copy();
        const c2 = other.c.copy();

        const axes = [
            a1.normWith(b1),
            b1.normWith(c1),
            c1.normWith(a1),
            a2.normWith(b2),
            b2.normWith(c2),
            c2.normWith(a2)
        ];

        let minOverlap = Number.POSITIVE_INFINITY;
        let maxOverlap = Number.NEGATIVE_INFINITY;
        let minOverlapAxis = null;

        for (const axis of axes) {
            const thisProj = this.project(axis, a1, b1, c1);
            const otherProj = other.project(axis, a2, b2, c2);

            const overlap = Math.min(thisProj.max, otherProj.max) - Math.max(thisProj.min, otherProj.min);

            if (overlap < 0) {
                return {
                    result: false,
                    other: other,
                    axes: {x: 0, y: 0}
                };
            } else if (overlap < minOverlap) {
                minOverlap = overlap;
                minOverlapAxis = axis;
            }

            if (overlap > maxOverlap)
                maxOverlap = overlap;
        }

        return {
            result: true,
            overlap: minOverlap,
            maxOverlap: maxOverlap,
            axes: minOverlapAxis
        };
    }

    /**
     * Projects points onto a specified axis and finds the minimum and maximum projection values.
     * @param {Vec2} axis Axis to project onto
     * @param {Vec2} a First point
     * @param {Vec2} b Second point
     * @param {Vec2} c Third point
     * @returns {{min: number, max: number}} Object containing minimum and maximum values.
     */
    project(axis, a, b, c) {
        const points = [a, b, c];
        let min = points[0].dot(axis);
        let max = min;

        for (let i = 1; i < 3; i++) {
            const projection = points[i].dot(axis);
            min = Math.min(min, projection);
            max = Math.max(max, projection);
        }

        return { min: min, max: max };
    }

    /**
     * Transform a triangle with a transformation matrix.
     * @param matrix Transformation matrix
     * @returns {Triangle} Transformed version of triangle
     */
    transform(matrix) {
        return new Triangle(
            this.a.transform(matrix),
            this.b.transform(matrix),
            this.c.transform(matrix)
        );
    }

    /**
     * Draw the triangles for debugging.
     * @param {CanvasRenderingContext2D} ctx The canvas context to draw on
     */
    drawDebug(ctx) {
        ctx.strokeStyle = $$.colors.debug_collision;
        ctx.lineWidth = 1.5;
        ctx.beginPath();

        const points = [this.a, this.b, this.c];

        for (let i = 0; i < 3; i++) {
            const curr = points[i];
            const next = points[(i + 1) % 3];

            ctx.moveTo(curr.x, curr.y);
            ctx.lineTo(next.x, next.y);
            ctx.stroke();
        }
    }
}

export default Triangle;