Source: rigid_shapes/rigid_rectangle_circle_collision.js

/* 
 * File: rigid_rectangle_circle_collision.js
 *
 *       Add circle/rectangle collision function to RigidRectangle
 */
"use strict";

import RigidRectangle from "./rigid_rectangle_collision.js";

/**
 * Determines if there is collision between the shapes
 * @memberof RigidRectangle
 * @param {vec2} v1 - the rectangle vertex that is closest to the center of the circle
 * @param {vec2} cirCenter - the center of the circle
 * @param {float} r - the radius of the circle
 * @param {CollisionInfo} info - used to store the collision info
 * @returns {boolean} true if there is collision between the 2 shapes
 */
RigidRectangle.prototype.checkCircRectVertex = function(v1, cirCenter, r, info) {
    // the center of circle is in corner region of mVertex[nearestEdge]
    let dist = vec2.length(v1);
    // compare the distance with radius to decide collision
    if (dist > r)
        return false;
    let radiusVec = [0, 0];
    let ptAtCirc = [0, 0];
    vec2.scale(v1, v1, 1/dist); // normalize
    vec2.scale(radiusVec, v1, -r);
    vec2.add(ptAtCirc, cirCenter, radiusVec);
    info.setInfo(r - dist, v1, ptAtCirc);
    return true;
}

/**
 * Check for collision between RigidRectangle and Circle
 * @memberof RigidRectangle
 * @param {RigidCircle} otherCir - circle to check for collision status against
 * @param {CollisionInfo} collisionInfo - Where the Collision Info is stored
 * @returns {boolean} true if collision occurs
 */
RigidRectangle.prototype.collideRectCirc = function (otherCir, collisionInfo) {
    let outside = false;
    let bestDistance = -Number.MAX_VALUE;
    let nearestEdge = 0; 
    let vToC = [0, 0];
    let projection = 0;
    let i = 0;
    let cirCenter = otherCir.getCenter();
    
    // Step A: Compute the nearest edge
    while ((!outside) && (i<4)) {
        // find the nearest face for center of circle        
        vec2.subtract(vToC, cirCenter, this.mVertex[i]);
        projection = vec2.dot(vToC, this.mFaceNormal[i]);
        if (projection > bestDistance) {
            outside = (projection > 0); // if projection < 0, inside
            bestDistance = projection;
            nearestEdge = i;
        }
        i++;
    }
    let dist;
    let radiusVec = [0, 0];
    let ptAtCirc = [0, 0];
    
    if (!outside) { // inside
        // Step B: The center of circle is inside of rectangle
        vec2.scale(radiusVec, this.mFaceNormal[nearestEdge], otherCir.mRadius);
        dist = otherCir.mRadius - bestDistance; // bestDist is -ve
        vec2.subtract(ptAtCirc, cirCenter, radiusVec);
        collisionInfo.setInfo(dist, this.mFaceNormal[nearestEdge], ptAtCirc);
        return true;
    }
    
    // the center of circle is outside of rectangle

    // v1 is from left vertex of face to center of circle 
    // v2 is from left vertex of face to right vertex of face
    let v1 = [0, 0], v2 = [0, 0];
    vec2.subtract(v1, cirCenter, this.mVertex[nearestEdge]);
    vec2.subtract(v2, this.mVertex[(nearestEdge + 1) % 4], this.mVertex[nearestEdge]);
    let dot = vec2.dot(v1, v2);

    if (dot < 0) {
        // Step C1: In Region RG1
        return this.checkCircRectVertex(v1, cirCenter, otherCir.mRadius, collisionInfo);
    } else {
        // Either in Region RG2 or RG3
        
        // v1 is from right vertex of face to center of circle 
        // v2 is from right vertex of face to left vertex of face
        vec2.subtract(v1, cirCenter, this.mVertex[(nearestEdge + 1) % 4]);
        vec2.scale(v2, v2, -1);
        dot = vec2.dot(v1, v2); 
        if (dot < 0) {
            // Step C2: In Region RG2
            return this.checkCircRectVertex(v1, cirCenter, otherCir.mRadius, collisionInfo);
        } else {
            // Step C3: In Region RG3 
            if (bestDistance < otherCir.mRadius) {
                vec2.scale(radiusVec, this.mFaceNormal[nearestEdge], otherCir.mRadius);
                dist = otherCir.mRadius - bestDistance;
                vec2.subtract(ptAtCirc, cirCenter, radiusVec);
                collisionInfo.setInfo(dist, this.mFaceNormal[nearestEdge], ptAtCirc);
                return true;
            } else {
                return false;
            }
        }
    }
    return true;
}

export default RigidRectangle;