找到矩形和圆形之间的交点
Find the intersection points between a rectangle and a circle
有一个矩形和一个圆,我想有一个函数可以 return 我得到它们碰撞点的坐标
像这样
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
}
这里有一个简单的方法来检查矩形的坐标是否在圆内。只需使用简单循环递增值即可。
let rect = { x: 40, y: 100, w: 100, h: 50 }
let mycircle = { x: 156, y: 156, r: 100 }
function isInside(circle_x, circle_y, rad, x, y) {
//formula x^2+y^2=r^2
if (
(x - circle_x) * (x - circle_x) + (y - circle_y) * (y - circle_y) <=
rad * rad
)
return true
else return false
}
function getIntersectionPoint(circle, rect) {
let coor = []
let notFullyInside = false
//for top
for (let index = rect.x; index < rect.x + rect.w; index++) {
if (isInside(circle.x, circle.y, circle.r, index, rect.y)) {
if (notFullyInside) {
coor.push({ x: index, y: rect.y })
break
}
} else {
notFullyInside = true
}
}
notFullyInside = false
//for left
for (let index = rect.y; index < rect.y + rect.h; index++) {
if (isInside(circle.x, circle.y, circle.r, rect.x, index)) {
if (notFullyInside) {
coor.push({ x: rect.x, y: index })
break
}
} else {
notFullyInside = true
}
}
notFullyInside = false
//for right
for (let index = rect.y; index < rect.y + rect.h; index++) {
if (isInside(circle.x, circle.y, circle.r, rect.x + rect.w, index)) {
if (notFullyInside) {
coor.push({ x: rect.x + rect.w, y: index })
break
}
} else {
notFullyInside = true
}
}
notFullyInside = false
//for bottom
for (let index = rect.x; index < rect.x + rect.w; index++) {
if (isInside(circle.x, circle.y, circle.r, index, rect.y + rect.h)) {
if (notFullyInside) {
coor.push({ x: index, y: rect.y + rect.h })
break
}
} else {
notFullyInside = true
}
}
return coor
}
console.log(getIntersectionPoint(mycircle, rect))
基于。唯一需要的代码是 intersections
函数(这是您的 detectCoords
函数)。如果您有任何疑问,请发表评论:-)
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>
表达式 n & 1
是一个 bitwise and。它选择基数 2 中 n
的“最右边”位。
> | 0 & 1 // 0b00
< | 0
> | 1 & 1 // 0b01
< | 1
> | 2 & 1 // 0b10
< | 0
> | 3 & 1 // 0b11
< | 1
您可以使用 n & 1
来检查一个数字是否为奇数。
> | 4 & 1 ? "odd" : "even"
< | "even"
高效实施指南:
WLOG 圆心是原点(如果不是你可以平移所有点)。然后假设在纵坐标 Y 和横坐标 X0 < X1 处的水平边缘。
如果 Y < -R 或 Y > R,则没有交集。
否则,检查 X0² > R² - Y² 和 X1² > R² - Y²。如果两者都为假,则该段完全位于内部。一假一真,只有一个交集。如果两个为真,则有两个交集。
交点的纵坐标为Y,所以横坐标为±√(R² - Y²)。根据以上inside/outside条件取负and/or正号。 (true/false 是 -,false/true 是 +,true/true 都是。)
对四个边重复上述步骤。 (您可以将水平边的条件重复用于垂直边。)
有一个矩形和一个圆,我想有一个函数可以 return 我得到它们碰撞点的坐标 像这样
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
}
这里有一个简单的方法来检查矩形的坐标是否在圆内。只需使用简单循环递增值即可。
let rect = { x: 40, y: 100, w: 100, h: 50 }
let mycircle = { x: 156, y: 156, r: 100 }
function isInside(circle_x, circle_y, rad, x, y) {
//formula x^2+y^2=r^2
if (
(x - circle_x) * (x - circle_x) + (y - circle_y) * (y - circle_y) <=
rad * rad
)
return true
else return false
}
function getIntersectionPoint(circle, rect) {
let coor = []
let notFullyInside = false
//for top
for (let index = rect.x; index < rect.x + rect.w; index++) {
if (isInside(circle.x, circle.y, circle.r, index, rect.y)) {
if (notFullyInside) {
coor.push({ x: index, y: rect.y })
break
}
} else {
notFullyInside = true
}
}
notFullyInside = false
//for left
for (let index = rect.y; index < rect.y + rect.h; index++) {
if (isInside(circle.x, circle.y, circle.r, rect.x, index)) {
if (notFullyInside) {
coor.push({ x: rect.x, y: index })
break
}
} else {
notFullyInside = true
}
}
notFullyInside = false
//for right
for (let index = rect.y; index < rect.y + rect.h; index++) {
if (isInside(circle.x, circle.y, circle.r, rect.x + rect.w, index)) {
if (notFullyInside) {
coor.push({ x: rect.x + rect.w, y: index })
break
}
} else {
notFullyInside = true
}
}
notFullyInside = false
//for bottom
for (let index = rect.x; index < rect.x + rect.w; index++) {
if (isInside(circle.x, circle.y, circle.r, index, rect.y + rect.h)) {
if (notFullyInside) {
coor.push({ x: index, y: rect.y + rect.h })
break
}
} else {
notFullyInside = true
}
}
return coor
}
console.log(getIntersectionPoint(mycircle, rect))
基于intersections
函数(这是您的 detectCoords
函数)。如果您有任何疑问,请发表评论:-)
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>
表达式 n & 1
是一个 bitwise and。它选择基数 2 中 n
的“最右边”位。
> | 0 & 1 // 0b00
< | 0
> | 1 & 1 // 0b01
< | 1
> | 2 & 1 // 0b10
< | 0
> | 3 & 1 // 0b11
< | 1
您可以使用 n & 1
来检查一个数字是否为奇数。
> | 4 & 1 ? "odd" : "even"
< | "even"
高效实施指南:
WLOG 圆心是原点(如果不是你可以平移所有点)。然后假设在纵坐标 Y 和横坐标 X0 < X1 处的水平边缘。
如果 Y < -R 或 Y > R,则没有交集。
否则,检查 X0² > R² - Y² 和 X1² > R² - Y²。如果两者都为假,则该段完全位于内部。一假一真,只有一个交集。如果两个为真,则有两个交集。
交点的纵坐标为Y,所以横坐标为±√(R² - Y²)。根据以上inside/outside条件取负and/or正号。 (true/false 是 -,false/true 是 +,true/true 都是。)
对四个边重复上述步骤。 (您可以将水平边的条件重复用于垂直边。)