Detect collision when a square is totally inside a circle

I have this function to detect collisions between a circle and a square

function detectCollision(square, circle) {
    let distX = Math.abs(circle.x - square.x);
    let distY = Math.abs(circle.y - square.y);

    if (distX > square.w / 2 + circle.r) return false;
    if (distY > square.w / 2 + circle.r) return false;

    if (distX <= square.w / 2) return true;
    if (distY <= square.w / 2) return true;

    let dx = distX - square.w / 2;
    let dy = distY - square.w / 2;
    return dx * dx + dy * dy <= circle.r * circle.r;
  }

It works perfectly but I need that in addition, the function detects if the square is totally inside the circle

enter image description here

it would just be like adding:

if (square is totally inside) console.log("its inside!");

Solution 1:

One way you can do this is to check if any of the square's corners go out of any of the circle's 4 quadrants respectively. For checking that we can compute the points along the circle's perimeter with sin and cos, and check if they are outside depending on the quadrant:

 <canvas height='500' width='500'></canvas>

 <script>
    const canvas = document.querySelector('canvas');
    const ctx = canvas.getContext('2d');

    let circle = {
        x: 200,
        y: 200,
        r: 100
    };

    let square = {
        x: 200,
        y: 120,
        side: 50
    };

    ctx.beginPath();
    ctx.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI);
    ctx.stroke();

    ctx.beginPath();
    ctx.rect(square.x, square.y, square.side, square.side);
    ctx.stroke();

    function detectContains(circle, square) {

        let sw = square.side;
        for(let deg=0; deg<=360; ++deg) {
            let x = circle.x + circle.r * Math.sin(Math.PI * 2 * deg / 360)
            let y = circle.y - circle.r * Math.cos(Math.PI * 2 * deg / 360)
            
            if(deg >= 0 && deg <= 90) {
                if(square.x+sw > x && square.y < y)
                    return false;
            } else if(deg >= 90 && deg <= 180) {
                if(square.x+sw > x && square.y+sw > y)
                    return false;
            } else if(deg >= 180 && deg <= 270) {
                if(square.x > x && square.y+sw > y)
                    return false;
            } else {
                if(square.x < x && square.y < y) {
                    return false;
                }
            }
            
        }

        return true;
    }

    console.log(detectContains(circle, square))


</script>

PS: This is done with what I have learned recently around graphics programming, I think the solution can be a lot better. I will try to update once I get a better solution.

Solution 2:

I would check the distance to the squares corners to see of they are all less than the radius.

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 300;
canvas.height = 300;
let canvasBounds = canvas.getBoundingClientRect();

class Rect {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.c = "blue";
  }
  draw() {
    ctx.strokeStyle = this.c;
    ctx.strokeRect(this.x, this.y, this.w, this.h);
  }
}

class Circle {
  constructor(x, y, r) {
    this.x = x;
    this.y = y;
    this.r = r;
  }
  draw() {
    ctx.strokeStyle = "black";
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
    ctx.stroke();
  }
}

let rect = new Rect(100, 100, 40, 40);
let circle = new Circle(100, 100, 75);

function detectCollision(square, circle) {
  let distX = Math.abs(circle.x - (square.x + square.w / 2));
  let distY = Math.abs(circle.y - (square.y + square.h / 2));
  
  let dx = distX - square.w / 2;
  let dy = distY - square.h / 2;

  if (
    distance(square.x, square.y, circle) < circle.r &&
    distance(square.x + square.w, square.y, circle) < circle.r &&
    distance(square.x + square.w, square.y + square.h, circle) < circle.r &&
    distance(square.x, square.y + square.h, circle) < circle.r
  ) {
    console.log("it's totally inside")
  } else if (dx * dx + dy * dy <= circle.r * circle.r) {
    console.log("it's not totally inside")
  } else {
    console.log("there's no collision")
  }
  
}

function distance(ptX, ptY, circle) {
  return Math.hypot(ptX - circle.x, ptY - circle.y);
}

canvas.addEventListener("mousemove", (e) => {
  rect.x = e.x - canvasBounds.x - rect.w / 2;
  rect.y = e.y - canvasBounds.y - rect.h / 2;
});

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  rect.draw();
  circle.draw();
  detectCollision(rect, circle);
  requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>

You may need to make it full screen to see the circle