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
}
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"