Vanesa Morilla
Vanesa Morilla

Reputation: 19

How to detect a collision between a circle and a rectangle?

There is a collision function to find the border of both divs (circle and rectangle), the first condition is working but not the rest, and up to this point I don't know what i'm doing wrong. Basically, the idea is for the circle not to go beyond the edges of the rectangle.

const $circle = document.getElementById("circle");
const $box = document.getElementById("box");
let mtop = 0;
let mleft = 0;
let boxTop  = 0;
let boxLeft  = 0;
let boxRight  = 0;
let boxBottom  = 0;
document.addEventListener("keydown", (e) =>{
   if(e.keyCode == "39") {
        mleft++;
        $circle.style.marginLeft = mleft +"px";
        colission();
    }  
    if(e.keyCode == "37"){
        mleft --;
        $circle.style.marginLeft = mleft +"px"
       // colission();
    }
    if(e.keyCode == "38"){
        mtop --;
        $circle.style.marginTop = mtop + "px";
        //colission();
    }
    if(e.keyCode == "40"){
        mtop ++;
        $circle.style.marginTop = mtop + "px";
        //colission();
    }
    e.preventDefault();
})
function colission(){
    let boxPos = $box.getBoundingClientRect();
    let boxTop  = boxPos.top;
    let boxLeft  = boxPos.left;
    let boxRight  = boxPos.right;
    let boxBottom  = boxPos.bottom;
     
    let circlePos = $circle.getBoundingClientRect();
    let circleTop = circlePos.top;
    let circleLeft = circlePos.left;
    let circleRight = circlePos.right;
    let circlebottom = circlePos.bottom;
    
    
    if(Math.round(circleRight) == Math.round(boxRight)){
         mleft = mleft - 2;
    }

    /*if(Math.round(circleLeft) == Math.round(boxLeft)){
        mleft = mleft - 2;
   }

    if(Math.round(circleTop) == Math.round(boxTop)){
        mtop = mtop -2;
   }

   if(Math.round(circlebottom) == Math.round(boxBottom)){
       mtop = mtop -2;
   }*/
}

Upvotes: 1

Views: 304

Answers (2)

Guerric P
Guerric P

Reputation: 31833

Here is a solution to your problem, the collision function returns true in case of a collision, else false. You can then implement some logic to prevent the last user action in case of a collision (try the demo with keyboard arrows):

document.addEventListener('keydown', (function() {
  function collision(box, circle) {
    const {
      top: boxTop,
      left: boxLeft,
      right: boxRight,
      bottom: boxBottom,
    } = box.getBoundingClientRect();
     
    const {
      top: circleTop,
      left: circleLeft,
      right: circleRight,
      bottom: circleBottom,
    } = circle.getBoundingClientRect();
    
    const circleRadius = (circleBottom - circleTop ) / 2;
    const circleCenterX = (circleLeft  + circleRight ) / 2;
    const circleCenterY = (circleTop  + circleBottom ) / 2;
    
    /**
     * Distance between the center of the circle and the nearest point of
     * the rectangle (on the X axis)
     */
    const deltaX = circleCenterX - Math.max(boxLeft, Math.min(circleCenterX, boxRight));

    /**
     * Distance between the center of the circle and the nearest point of
     * the rectangle (on the Y axis)
     */
    const deltaY = circleCenterY - Math.max(boxTop, Math.min(circleCenterY, boxBottom));

    /**
     * Is the distance between the center of the circle and the nearest point
     * of the rectangle inferior to the circle radius ?
     */
    return deltaX ** 2 + deltaY ** 2 < circleRadius ** 2;
  }

  const circle = document.getElementById('circle');
  const rectangle = document.getElementById('rectangle');

  let circleTop = 26;
  let circleLeft = 8;

  return function(e) {
    e.preventDefault();
    if(e.keyCode === 37) {
      circleLeft -= 1;
    }
    if(e.keyCode === 38) {
      circleTop -= 1;
    }
    if(e.keyCode === 39) {
      circleLeft += 1;
    }
    if(e.keyCode === 40) {
      circleTop += 1;
    }
    circle.style.top = `${circleTop}px`;
    circle.style.left = `${circleLeft}px`;

    if (collision(rectangle, circle)) {
      rectangle.style['background-color'] = 'red';
      circle.style['background-color'] = 'red';
    }
    else {
      rectangle.style['background-color'] = 'blue';
      circle.style['background-color'] = 'blue';
    }
  }
})());
.shape {
  position: absolute;
  background-color: blue;
}

#circle {
  border-radius: 50%;
  height: 20px;
  width: 20px;
}

#rectangle {
  height: 20px;
  width: 50px;
  top: 40px;
  left: 40px;
}
<div>Use the keyboard arrows to move the circle</div>
<div class="shape" id="circle"></div>
<div class="shape" id="rectangle"></div>

Edit: The actual question is about a circle that should not go outside a rectangle

Then just check if the boundaries of the circle are inside the boundaries of the rectangle:

document.addEventListener('keydown', (function() {
  function isInInterval(value, interval) {
    const [min, max] = interval;
    return value >= min && value <= max;
  }

  function collision(box, circle) {
    const {
      top: boxTop,
      left: boxLeft,
      right: boxRight,
      bottom: boxBottom,
    } = box.getBoundingClientRect();
     
    const {
      top: circleTop,
      left: circleLeft,
      right: circleRight,
      bottom: circleBottom,
    } = circle.getBoundingClientRect();

    return !(
        isInInterval(circleLeft, [boxLeft, boxRight]) &&
        isInInterval(circleRight, [boxLeft, boxRight]) &&
        isInInterval(circleTop, [boxTop, boxBottom]) &&
        isInInterval(circleBottom, [boxTop, boxBottom])
      );
  }

  const circle = document.getElementById('circle');
  const rectangle = document.getElementById('rectangle');

  let circleTop = 26;
  let circleLeft = 8;

  return function(e) {
    e.preventDefault();
    if(e.keyCode === 37) {
      circleLeft -= 1;
    }
    if(e.keyCode === 38) {
      circleTop -= 1;
    }
    if(e.keyCode === 39) {
      circleLeft += 1;
    }
    if(e.keyCode === 40) {
      circleTop += 1;
    }
    circle.style.top = `${circleTop}px`;
    circle.style.left = `${circleLeft}px`;

    if (collision(rectangle, circle)) {
      rectangle.style['background-color'] = 'red';
      circle.style['background-color'] = 'red';
    }
    else {
      rectangle.style['background-color'] = 'blue';
      circle.style['background-color'] = 'blue';
    }
  }
})());
.shape {
  position: absolute;
  background-color: red;
}

#circle {
  border-radius: 50%;
  height: 20px;
  width: 20px;
}

#rectangle {
  height: 20px;
  width: 50px;
  top: 40px;
  left: 40px;
}
<div>Use the keyboard arrows to move the circle</div>
<div class="shape" id="circle"></div>
<div class="shape" id="rectangle"></div>

Upvotes: 1

Vanesa Morilla
Vanesa Morilla

Reputation: 19

thanks for your answer, I could already solve the problem

const $circle = document.getElementById("circle");
const $box = document.getElementById("box");

let mtop = 0;
let mleft = 0;

let boxTop  = 0;
let boxLeft  = 0;
let boxRight  = 0;
let boxBottom  = 0;

document.addEventListener("keydown", (e) =>{
    e.preventDefault();

        let boxPos = $box.getBoundingClientRect();
        let boxTop  = boxPos.top;
        let boxLeft  = boxPos.left;
        let boxRight  = boxPos.right;
        let boxBottom  = boxPos.bottom;
        
        let circlePos = $circle.getBoundingClientRect();
        let circleTop = circlePos.top;
        let circleLeft = circlePos.left;
        let circleRight = circlePos.right;
        let circlebottom = circlePos.bottom;
        

    if(e.keyCode === 39) {
        mleft++;
        $circle.style.marginLeft = mleft +"px";
        if(Math.trunc(circleRight) === Math.trunc(boxRight)){
            mleft = mleft - 5;
       } 
    }  
    
    if(e.keyCode === 37){
        mleft--;
        $circle.style.marginLeft = mleft +"px"
        if(Math.trunc(circleLeft) === Math.trunc(boxLeft)){
            mleft = mleft + 5;
       }
    }

    if(e.keyCode === 38){
        mtop--;
        $circle.style.marginTop = mtop + "px";
        if(Math.trunc(circleTop) === Math.trunc(boxTop)){
            mtop = mtop + 5;
       }
    }

    if(e.keyCode === 40){
        mtop++;
        $circle.style.marginTop = mtop + "px";
        if(Math.trunc(circlebottom) === Math.trunc(boxBottom)){
            mtop = mtop - 5;
        }
    }
})
:root {
  --main-color: red;
  --second-color: blue;
}

.container {
    background-color: var(--main-color);
    border: black;
    width: 50%;
    height: 60%;
    position: absolute;
}

#circle{
    position: absolute;
    width: 60px;
    height: 60px;
    left: 0;
    right: 0;
    border-radius: 100%;
    background-color: var(--second-color);
    padding: 10px;
}
<div id="box" class="container">
   <div id="circle"></div>
</div>

Upvotes: 0

Related Questions