Find the intersection points between a rectangle and a circle

having a rectangle and a circle, I would like to have a function that would return me the coordinates of the points where they collide something like this

let myrect = { x: 100, y: 100, w: 100, h: 50 };
let mycircle = { x: 156, y: 156, r: 100 };

function detectCoords(rect, circle) {
    //do something
    return [{ x: 5, y: 2}, { x: 3, y: 7}] //example
}

enter image description here


Based on Wyck's comment. The only required code is the intersections function (which is your detectCoords function). If you have questions, drop a comment :-)

function intersections (rect, circle) {
  var y_top     = rect.y - rect.h / 2;
  var x_right   = rect.x + rect.w / 2;
  var y_bottom  = rect.y + rect.h / 2;
  var x_left    = rect.x - rect.w / 2;
  return [
    // absolute coordinates
    // of all the `rect` edges
    /* 0 even */ y_top,
    /* 1 odd  */ x_right,
    /* 2 even */ y_bottom,
    /* 3 odd  */ x_left
  ].map(function (x_or_y, i) {
    // relative coordinate
    // of one `rect` edge
    return x_or_y - (
      // `i & 1` is 0 or 1
      circle["yx"[i & 1]]
    );
  }).map(function (x_or_y, i) {
    // edge out of circle
    if (Math.abs(x_or_y) > circle.r) {
      return [];
    } 
    // edge and circle intersect
    else {
      let y_or_x, x1, y1, x2, y2;
      y_or_x = Math.sqrt(
        circle.r ** 2 - x_or_y ** 2
      );
      i = i & 1; // 0 or 1
      x1 = [y_or_x, x_or_y][i];
      y1 = [x_or_y, y_or_x][i];
      x2 = x1 * (i ? +1 : -1);
      y2 = y1 * (i ? -1 : +1);
      // two elligible points
      // with absolute coordinates
      return [{
        x : circle.x + x1,
        y : circle.y + y1
      }, {
        x : circle.x + x2,
        y : circle.y + y2
      }];
    }
  }).reduce(function (acc, xys, i) {
    var k, min, max;
    i = i & 1; // 0 or 1
    k = "xy"[i];
    min = [x_left, y_top][i];
    max = [x_right, y_bottom][i];
    return acc.concat(xys.filter(function (xy) {
      // `xy` is on the edge ? yes : no
      return xy[k] >= min && xy[k] <= max;
    }));
  }, []);
}

// optional code

onload = function () {
  var canvasEl = getEl("canvas");
  var ctx = canvasEl.getContext("2d");
  canvasEl.width = 400;
  canvasEl.height = 300;
  draw(
    ctx,
    readRect(),
    readCircle()
  );
  onSubmit("form", function (ev) {
    ev.preventDefault();
    ctx.clearRect(
      0, 0,
      canvasEl.width,
      canvasEl.height
    );
    draw(
      ctx,
      readRect(),
      readCircle()
    );
  });
}

function readRect () {
  var x, y, w, h;
  var rectXyEl = getEl("rect-xy");
  var rectWhEl = getEl("rect-wh");
  [x, y] = readPair(rectXyEl);
  [w, h] = readPair(rectWhEl);
  return { x : x, y : y, w : w, h : h };
}

function readCircle () {
  var x, y, r;
  var circleXyEl = getEl("circle-xy");
  var circleREl = getEl("circle-r");
  [x, y] = readPair(circleXyEl);
  r = parseInt(circleREl.value, 10);
  return { x : x, y : y, r : r };
}

function readPair (el) {
  return el.value.split(" ").map(
    (x) => parseInt(x, 10)
  );
}

function draw (ctx, rect, circle) {
  drawRect(ctx, rect);
  drawCircle(ctx, circle);
  drawIntersections(ctx, rect, circle);
}

function drawRect (ctx, rect) {
  ctx.beginPath();
  ctx.rect(
    rect.x - rect.w / 2,
    rect.y - rect.h / 2,
    rect.w, rect.h
  );
  ctx.stroke();
}

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

function drawIntersections (ctx, rect, circle) {
  for (let xy of intersections(rect, circle)) {
    ctx.beginPath();
    ctx.arc(xy.x, xy.y, 3, 0, 2 * Math.PI, true);
    ctx.stroke();
  }
}

function onSubmit (id, f) {
  getEl(id).addEventListener("submit", f);
}

function getEl (id) {
  return document.getElementById(id);
}
body {
  margin: .5em;
  background: #ddd;
}
input[type=text] {
  width: 60px;
}
input[type=submit] {
  margin-top: .5em;
}
.column {
  float: left;
}
.column:first-child {
  background: white;
  margin-right: .5em;
  padding: .5em;
  width: 90px;
}
<div class="column">
  <form id="form">
    rect x y <input
      type="text"
      id="rect-xy"
      value="100 100"
    >
    rect w h <input
      type="text"
      id="rect-wh"
      value="130 130"
    >
    circle x y <input
      type="text"
      id="circle-xy"
      value="100 100"
    >
    circle r <input
      type="text"
      id="circle-r"
      value="75"
    >
    <input type="submit">
  </form>
</div>
<div class="column">
  <canvas id="canvas" style="background:white"></canvas>
</div>

The expression n & 1 is a bitwise and. It picks the "rightmost" bit of n in base 2.

> | 0 & 1 // 0b00
< | 0
> | 1 & 1 // 0b01
< | 1
> | 2 & 1 // 0b10
< | 0
> | 3 & 1 // 0b11
< | 1

You can use n & 1 to check whether a number is odd.

> | 4 & 1 ? "odd" : "even"
< | "even"