处理:直线与圆的交点距离
Processing: Distance of intersection between line and circle
现在,我知道有人问过类似的问题。但是 none 的答案帮助我找到了我需要的结果。
以下情况:
我们有一条带有起点 (PO) 的线,给定为 lx
、ly
。我们还有一个 angle
表示它退出 PO 的线,其中 0° 表示水平向右,正度数表示顺时针。 angle
在 [0;360[
中。此外,我们还有线的长度,因为它不是无限长,如 len
.
还有一个给定中心点(CP)的圆,给定为cx
,cy
。半径为 cr
.
我现在需要一个将这些数字作为参数的函数,returns线和圆之间最近的交点到 PO 的距离,如果没有交点则为 -1。
我目前的做法如下:
float getDistance(float lx, float ly, float angle, float len, float cx, float cy, float cr) {
float nlx = lx - cx;
float nly = ly - cy;
float m = tan(angle);
float b = (-lx) * m;
// a = m^2 + 1
// b = 2 * m * b
// c = b^2 - cr^2
float[] x_12 = quadraticFormula(sq(m) + 1, 2*m*b, sq(b) - sq(cr));
// if no intersections
if (Float.isNaN(x_12[0]) && Float.isNaN(x_12[1]))
return -1;
float distance;
if (Float.isNaN(x_12[0])) {
distance = (x_12[1] - nlx) / cos(angle);
} else {
distance = (x_12[0] - nlx) / cos(angle);
}
if (distance <= len) {
return distance;
}
return -1;
}
// solves for x
float[] quadraticFormula(float a, float b, float c) {
float[] results = new float[2];
results[0] = (-b + sqrt(sq(b) - 4 * a * c)) / (2*a);
results[1] = (-b - sqrt(sq(b) - 4 * a * c)) / (2*a);
return results;
}
但结果并不如人意。有时我确实会返回一个距离,但这很少是正确的,通常甚至没有发生交叉点。虽然应该有一个,但大多数时候没有返回交集。
如有任何帮助,我们将不胜感激。
编辑:
多亏了MBo的回答,我终于找到了解决办法。这是我完成的 getDistance(...)
-function 的内容 - 也许有人可以从中得到帮助:
float nlx = lx - cx;
float nly = ly - cy;
float dx = cos(angle);
float dy = sin(angle);
float[] results = quadraticFormula(1, 2*(nlx*dx + nly*dy), sq(nlx)+sq(nly)-sq(cr));
float dist = -1;
if (results[0] >= 0 && results[0] <= len)
dist = results[0];
if (results[1] >= 0 && results[1] <= len && results[1] < results[0])
dist = results[1];
return dist;
利用你的nlx,nly,我们可以建立线段的参数方程
dx = Cos(angle)
dy = Sin(Angle)
x = nlx + t * dx
y = nly + t * dy
与圆周相交的条件:
(nlx + t * dx)^2 + (nly + t * dy)^2 = cr^2
t^2 * (dx^2 + dy^2) + t * (2*nlx*dx + 2*nly*dy) + nlx^2+nly^2-cr^2 = 0
所以我们有未知参数 t 的二次方程
a = 1
b = 2*(nlx*dx + nly*dy)
c = nlx^2+nly^2-cr^2
解二次方程,求t是否在0..len范围内。
// https://openprocessing.org/sketch/8009#
// by https://openprocessing.org/user/54?view=sketches
float circleX = 200;
float circleY = 200;
float circleRadius = 100;
float lineX1 = 350;
float lineY1 = 350;
float lineX2, lineY2;
void setup() {
size(400, 400);
ellipseMode(RADIUS);
smooth();
}
void draw() {
background(204);
lineX2 = mouseX;
lineY2 = mouseY;
if (circleLineIntersect(lineX1, lineY1, lineX2, lineY2, circleX, circleY, circleRadius) == true) {
noFill();
}
else {
fill(255);
}
ellipse(circleX, circleY, circleRadius, circleRadius);
line(lineX1, lineY1, lineX2, lineY2);
}
// Code adapted from Paul Bourke:
// http://local.wasp.uwa.edu.au/~pbourke/geometry/sphereline/raysphere.c
boolean circleLineIntersect(float x1, float y1, float x2, float y2, float cx, float cy, float cr ) {
float dx = x2 - x1;
float dy = y2 - y1;
float a = dx * dx + dy * dy;
float b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
float c = cx * cx + cy * cy;
c += x1 * x1 + y1 * y1;
c -= 2 * (cx * x1 + cy * y1);
c -= cr * cr;
float bb4ac = b * b - 4 * a * c;
//println(bb4ac);
if (bb4ac < 0) { // Not intersecting
return false;
}
else {
float mu = (-b + sqrt( b*b - 4*a*c )) / (2*a);
float ix1 = x1 + mu*(dx);
float iy1 = y1 + mu*(dy);
mu = (-b - sqrt(b*b - 4*a*c )) / (2*a);
float ix2 = x1 + mu*(dx);
float iy2 = y1 + mu*(dy);
// The intersection points
ellipse(ix1, iy1, 10, 10);
ellipse(ix2, iy2, 10, 10);
float testX;
float testY;
// Figure out which point is closer to the circle
if (dist(x1, y1, cx, cy) < dist(x2, y2, cx, cy)) {
testX = x2;
testY = y2;
} else {
testX = x1;
testY = y1;
}
if (dist(testX, testY, ix1, iy1) < dist(x1, y1, x2, y2) || dist(testX, testY, ix2, iy2) < dist(x1, y1, x2, y2)) {
return true;
} else {
return false;
}
}
}
现在,我知道有人问过类似的问题。但是 none 的答案帮助我找到了我需要的结果。
以下情况:
我们有一条带有起点 (PO) 的线,给定为 lx
、ly
。我们还有一个 angle
表示它退出 PO 的线,其中 0° 表示水平向右,正度数表示顺时针。 angle
在 [0;360[
中。此外,我们还有线的长度,因为它不是无限长,如 len
.
还有一个给定中心点(CP)的圆,给定为cx
,cy
。半径为 cr
.
我现在需要一个将这些数字作为参数的函数,returns线和圆之间最近的交点到 PO 的距离,如果没有交点则为 -1。
我目前的做法如下:
float getDistance(float lx, float ly, float angle, float len, float cx, float cy, float cr) {
float nlx = lx - cx;
float nly = ly - cy;
float m = tan(angle);
float b = (-lx) * m;
// a = m^2 + 1
// b = 2 * m * b
// c = b^2 - cr^2
float[] x_12 = quadraticFormula(sq(m) + 1, 2*m*b, sq(b) - sq(cr));
// if no intersections
if (Float.isNaN(x_12[0]) && Float.isNaN(x_12[1]))
return -1;
float distance;
if (Float.isNaN(x_12[0])) {
distance = (x_12[1] - nlx) / cos(angle);
} else {
distance = (x_12[0] - nlx) / cos(angle);
}
if (distance <= len) {
return distance;
}
return -1;
}
// solves for x
float[] quadraticFormula(float a, float b, float c) {
float[] results = new float[2];
results[0] = (-b + sqrt(sq(b) - 4 * a * c)) / (2*a);
results[1] = (-b - sqrt(sq(b) - 4 * a * c)) / (2*a);
return results;
}
但结果并不如人意。有时我确实会返回一个距离,但这很少是正确的,通常甚至没有发生交叉点。虽然应该有一个,但大多数时候没有返回交集。
如有任何帮助,我们将不胜感激。
编辑:
多亏了MBo的回答,我终于找到了解决办法。这是我完成的 getDistance(...)
-function 的内容 - 也许有人可以从中得到帮助:
float nlx = lx - cx;
float nly = ly - cy;
float dx = cos(angle);
float dy = sin(angle);
float[] results = quadraticFormula(1, 2*(nlx*dx + nly*dy), sq(nlx)+sq(nly)-sq(cr));
float dist = -1;
if (results[0] >= 0 && results[0] <= len)
dist = results[0];
if (results[1] >= 0 && results[1] <= len && results[1] < results[0])
dist = results[1];
return dist;
利用你的nlx,nly,我们可以建立线段的参数方程
dx = Cos(angle)
dy = Sin(Angle)
x = nlx + t * dx
y = nly + t * dy
与圆周相交的条件:
(nlx + t * dx)^2 + (nly + t * dy)^2 = cr^2
t^2 * (dx^2 + dy^2) + t * (2*nlx*dx + 2*nly*dy) + nlx^2+nly^2-cr^2 = 0
所以我们有未知参数 t 的二次方程
a = 1
b = 2*(nlx*dx + nly*dy)
c = nlx^2+nly^2-cr^2
解二次方程,求t是否在0..len范围内。
// https://openprocessing.org/sketch/8009#
// by https://openprocessing.org/user/54?view=sketches
float circleX = 200;
float circleY = 200;
float circleRadius = 100;
float lineX1 = 350;
float lineY1 = 350;
float lineX2, lineY2;
void setup() {
size(400, 400);
ellipseMode(RADIUS);
smooth();
}
void draw() {
background(204);
lineX2 = mouseX;
lineY2 = mouseY;
if (circleLineIntersect(lineX1, lineY1, lineX2, lineY2, circleX, circleY, circleRadius) == true) {
noFill();
}
else {
fill(255);
}
ellipse(circleX, circleY, circleRadius, circleRadius);
line(lineX1, lineY1, lineX2, lineY2);
}
// Code adapted from Paul Bourke:
// http://local.wasp.uwa.edu.au/~pbourke/geometry/sphereline/raysphere.c
boolean circleLineIntersect(float x1, float y1, float x2, float y2, float cx, float cy, float cr ) {
float dx = x2 - x1;
float dy = y2 - y1;
float a = dx * dx + dy * dy;
float b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
float c = cx * cx + cy * cy;
c += x1 * x1 + y1 * y1;
c -= 2 * (cx * x1 + cy * y1);
c -= cr * cr;
float bb4ac = b * b - 4 * a * c;
//println(bb4ac);
if (bb4ac < 0) { // Not intersecting
return false;
}
else {
float mu = (-b + sqrt( b*b - 4*a*c )) / (2*a);
float ix1 = x1 + mu*(dx);
float iy1 = y1 + mu*(dy);
mu = (-b - sqrt(b*b - 4*a*c )) / (2*a);
float ix2 = x1 + mu*(dx);
float iy2 = y1 + mu*(dy);
// The intersection points
ellipse(ix1, iy1, 10, 10);
ellipse(ix2, iy2, 10, 10);
float testX;
float testY;
// Figure out which point is closer to the circle
if (dist(x1, y1, cx, cy) < dist(x2, y2, cx, cy)) {
testX = x2;
testY = y2;
} else {
testX = x1;
testY = y1;
}
if (dist(testX, testY, ix1, iy1) < dist(x1, y1, x2, y2) || dist(testX, testY, ix2, iy2) < dist(x1, y1, x2, y2)) {
return true;
} else {
return false;
}
}
}