Source: RigidShapes/RigidRectangleCircle_Collision.js

/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/*jslint node: true, vars: true, evil: true, bitwise: true */
"use strict";
/* global RigidRectangle, vec2 */

/**
 * Determines if there is collision between the shapes
 * @memberOf RigidRectangle
 * @param {float[]} v The rectangle vertex that is closest to the center of the circle
 * @param {float[]} circPt The center of the circle
 * @param {float} r The radius of the circle
 * @param {CollisionInfo} info Used to store the collision info
 * @returns {Boolean} If there is collision between the 2 shapes
 */
RigidRectangle.prototype.checkCircRecVertex = function(v, circPt, r, info) {
    //the center of circle is in corner region of mVertex[nearestEdge]
    var dis = vec2.length(v);
    //compare the distance with radium to decide collision
    if (dis > r)
        return false;
    var radiusVec = [0, 0];
    var ptAtCirc = [0, 0];
    vec2.scale(v, v, 1/dis); // normalize
    vec2.scale(radiusVec, v, -r);
    vec2.add(ptAtCirc, circPt, radiusVec);
    info.setInfo(r - dis, v, ptAtCirc);
    return true;
};

/**
 * Check for collision between RigidRectangle and Circle
 * @param {Circle} otherCir circle to check for collision status against
 * @param {CollisionInfo} collisionInfo Where the Collision Info is stored
 * @returns {Boolean} true if collision occurs
 * @memberOf RigidRectangle
 */
RigidRectangle.prototype.collideRectCirc = function (otherCir, collisionInfo) {
    var outside = false;
    var bestDistance = -Number.MAX_VALUE;
    var nearestEdge = 0; 
    var vToC = [0, 0];
    var circ2Pos = [0, 0], projection;
    var i = 0;
    while ((!outside) && (i<4)) {
        //find the nearest face for center of circle        
        circ2Pos = otherCir.getCenter();
        vec2.subtract(vToC, circ2Pos, 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++;
    }
    var dis;
    var radiusVec = [0, 0];
    var ptAtCirc = [0, 0];
    
    if (!outside) { // inside
        //the center of circle is inside of rectangle
        vec2.scale(radiusVec, this.mFaceNormal[nearestEdge], otherCir.mRadius);
        dis = otherCir.mRadius - bestDistance; // bestDist is -ve
        vec2.subtract(ptAtCirc, circ2Pos, radiusVec);
        collisionInfo.setInfo(dis, 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
    var v1 = [0, 0], v2 = [0, 0];
    vec2.subtract(v1, circ2Pos, this.mVertex[nearestEdge]);
    vec2.subtract(v2, this.mVertex[(nearestEdge + 1) % 4], this.mVertex[nearestEdge]);
    var dot = vec2.dot(v1, v2);

    if (dot < 0) {
        return this.checkCircRecVertex(v1, circ2Pos, otherCir.mRadius, collisionInfo);
    } else {
        //the center of circle is in corner region of mVertex[nearestEdge+1]
        
        //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, circ2Pos, this.mVertex[(nearestEdge + 1) % 4]);
        vec2.scale(v2, v2, -1);
        dot = vec2.dot(v1, v2); 
        if (dot < 0) {
            return this.checkCircRecVertex(v1, circ2Pos, otherCir.mRadius, collisionInfo);
        } else {
            //the center of circle is in face region of face[nearestEdge]
            if (bestDistance < otherCir.mRadius) {
                vec2.scale(radiusVec, this.mFaceNormal[nearestEdge], otherCir.mRadius);
                dis = otherCir.mRadius - bestDistance;
                vec2.subtract(ptAtCirc, circ2Pos, radiusVec);
                collisionInfo.setInfo(dis, this.mFaceNormal[nearestEdge], ptAtCirc);
                return true;
            } else {
                return false;
            }
        }
    }
    return true;
};