Source: rigid_shapes/rigid_rectangle_collision.js

/* File: rigid_rectangle_collision.js
 *       Adds the collision functions for RigidRectangle class
 */
"use strict";

import CollisionInfo from "./collision_info.js";
import RigidRectangle from "./rigid_rectangle_main.js";

/**
 * default constructor
 * @ignore
 * @returns {SupportStruct}
 */
class SupportStruct {
    constructor() {
        this.mSupportPoint = null;
        this.mSupportPointDist = 0;
    }
}

// temp work area to conserve run time dynamic allocation cost
let mTmpSupport = new SupportStruct();
let mCollisionInfoR1 = new CollisionInfo();
let mCollisionInfoR2 = new CollisionInfo();

/**
 * Decides on which collision function to call based on the type of shape passed
 * @memberof RigidRectangle 
 * @param {RigidShape} otherShape - The other shape to test collision
 * @param {CollisionInfo} collisionInfo - Where the collision information is stored
 * @returns {boolean} the results of the collision
 */
RigidRectangle.prototype.collisionTest = function (otherShape, collisionInfo) {
    let status = false;
    if (otherShape.mType === "RigidCircle") {
        status = this.collideRectCirc(otherShape, collisionInfo);
    } else {
        status = this.collideRectRect(this, otherShape, collisionInfo);
    }
    return status;
}

/**
 * Calculates a support point for a point on the edge of this RigidRectangle
 * @memberof RigidRectangle
 * @param {vec2} dir - the direction of the support point 
 * @param {vec2} ptOnEdge - a point on the edge of this RigidRectangle 
 */
RigidRectangle.prototype.findSupportPoint = function (dir, ptOnEdge) {
    // the longest project length
    let vToEdge = [0, 0];
    let projection;

    mTmpSupport.mSupportPointDist = -Number.MAX_VALUE;
    mTmpSupport.mSupportPoint = null;
    // check each vector of other object
    for (let i = 0; i < this.mVertex.length; i++) {
        vec2.subtract(vToEdge, this.mVertex[i], ptOnEdge);
        projection = vec2.dot(vToEdge, dir);
        
        // find the longest distance with certain edge
        // dir is -n direction, so the distance should be positive       
        if ((projection > 0) && (projection > mTmpSupport.mSupportPointDist)) {
            mTmpSupport.mSupportPoint = this.mVertex[i];
            mTmpSupport.mSupportPointDist = projection;
        }
    }
}


/**
 * Find the shortest axis of penetration between this RigidRectangle and another
 * @memberOf RigidRectangle
 * @param {RigidRectangle} otherRect - the other rectangle being tested
 * @param {CollisionInfo} collisionInfo - Record of the collision information
 * @returns {boolean} true if there is overlap in all four directions.
 */
RigidRectangle.prototype.findAxisLeastPenetration = function (otherRect, collisionInfo) {
    let n;
    let supportPoint;

    let bestDistance = Number.MAX_VALUE;
    let bestIndex = null;

    let hasSupport = true;
    let i = 0;

    let dir = [0, 0];
    while ((hasSupport) && (i < this.mFaceNormal.length)) {
        // Retrieve a face normal from A
        n = this.mFaceNormal[i];

        // use -n as direction and the vertex on edge i as point on edge    
        vec2.scale(dir, n, -1);
        let ptOnEdge = this.mVertex[i];
        // find the support on B
        // the point has longest distance with edge i 
        otherRect.findSupportPoint(dir, ptOnEdge);
        hasSupport = (mTmpSupport.mSupportPoint !== null);
        
        // get the shortest support point depth
        if ((hasSupport) && (mTmpSupport.mSupportPointDist < bestDistance)) {
            bestDistance = mTmpSupport.mSupportPointDist;
            bestIndex = i;
            supportPoint = mTmpSupport.mSupportPoint;
        }
        i = i + 1;
    }
    if (hasSupport) {
        // all four directions have support point
        let bestVec = [0, 0];
        vec2.scale(bestVec, this.mFaceNormal[bestIndex], bestDistance);
        let atPos = [0, 0];
        vec2.add(atPos, supportPoint, bestVec);
        collisionInfo.setInfo(bestDistance, this.mFaceNormal[bestIndex], atPos);
    }
    return hasSupport;
}
    
/**
 * Check for collision between a RigidRectangle and another RigidRectangle
 * @memberof RigidRectangle
 * @param {RigidRectangle} r1 - RigidRectangle object to check for collision status
 * @param {RigidRectangle} r2 - RigidRectangle object to check for collision status against
 * @param {CollisionInfo} collisionInfo - the information object for the collision
 * @returns {boolean} true if collision occurs
 */   
RigidRectangle.prototype.collideRectRect = function (r1, r2, collisionInfo) {
    let status1 = false;
    let status2 = false;

    // find Axis of Separation for both rectangle
    status1 = r1.findAxisLeastPenetration(r2, mCollisionInfoR1);

    if (status1) {
        status2 = r2.findAxisLeastPenetration(r1, mCollisionInfoR2);
        if (status2) {
            let depthVec = [0, 0];
            // if both of rectangles are overlapping, choose the shorter normal as the normal       
            if (mCollisionInfoR1.getDepth() < mCollisionInfoR2.getDepth()) {
                vec2.scale(depthVec, mCollisionInfoR1.getNormal(), mCollisionInfoR1.getDepth());
                let pos = [0, 0];
                vec2.subtract(pos, mCollisionInfoR1.mStart, depthVec);
                collisionInfo.setInfo(mCollisionInfoR1.getDepth(), mCollisionInfoR1.getNormal(), pos);
            } else {
                vec2.scale(depthVec, mCollisionInfoR2.getNormal(), -1);
                collisionInfo.setInfo(mCollisionInfoR2.getDepth(), depthVec, mCollisionInfoR2.mStart);
            }
        } 
    }
    return status1 && status2;
}

export default RigidRectangle;