处理:直线与圆的交点距离

Processing: Distance of intersection between line and circle

现在,我知道有人问过类似的问题。但是 none 的答案帮助我找到了我需要的结果。

以下情况:

我们有一条带有起点 (PO) 的线,给定为 lxly。我们还有一个 angle 表示它退出 PO 的线,其中 0° 表示水平向右,正度数表示顺时针。 angle[0;360[ 中。此外,我们还有线的长度,因为它不是无限长,如 len.

还有一个给定中心点(CP)的圆,给定为cxcy。半径为 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;
}
}
}