这段用于确定圆和线段是否相交的代码正确吗?

2024-04-17

显然很难找到一条线是否存在的答案segment和圆相交。例如,如果你用谷歌搜索,你会发现这个问题 https://stackoverflow.com/questions/1073336/circle-line-segment-collision-detection-algorithm甚至最上面的两个答案似乎也是错误的。

接受的答案有一条评论说:This seems to compute the intersection of a circle with a line, not a segment向下滚动到下一个答案,您会发现另一条评论说Isn't this answer in incomplete? It finds whether a circle and line intersect, not a line segment.

然后我尝试寻找一个函数来确定是否只是一个segment与圆相交,但无济于事。我能找到的最好的是伪代码解释在这里 https://stackoverflow.com/a/9053697/962155.

我尝试调整他的代码,虽然它似乎有效,但似乎过于冗长,我不确定我的实现是否正确。我想问这是否正确,如果正确,是否确实没有更好的方法来确定这一点?确定线段和圆是否相交的理想方法是什么?请注意,我只需要知道if它们相交,而不是相交的地方。

我在下面提供了完整的演示复制品,以便您也可以将其可视化。

function lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) {
  let deltaX = x2 - x1;
  let deltaY = y2 - y1;

  let mag = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

  let unitX = deltaX / mag;
  let unitY = deltaY / mag;

  let d = (cx - x1) * unitY - (cy - y1) * unitX;

  if (d < -r || d > r) { return false; }

  let x1CXDelta = x1 - cx;
  let y1CYDelta = y1 - cy;

  let x2CXDelta = x2 - cx;
  let y2CYDelta = y2 - cy;

  let pointOneWithinCircle = x1CXDelta * x1CXDelta + y1CYDelta * y1CYDelta < r * r;
  if (pointOneWithinCircle) { return true; }

  let pointTwoWithinCircle = x2CXDelta * x2CXDelta + y2CYDelta * y2CYDelta < r * r;
  if (pointTwoWithinCircle) { return true; }

  let foo = unitX * x1 + unitY * y1;
  let bar = unitX * cx + unitY * cy;
  let baz = unitX * x2 + unitY * y2;

  return (foo < bar && bar < baz) || (baz < bar && bar < foo); 
}

let ctx = document.querySelector("canvas").getContext("2d");

function drawCircle(xCenter, yCenter, radius) {
  ctx.beginPath();
  ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI);
  ctx.fill();
}

function drawLine(x1, y1, x2, y2) {
  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.stroke();
}

let circleX = 340;
let circleY = 250;
let circleR = 60;

let lineX1 = 50;
let lineY1 = 350;
let lineX2 = 285;
let lineY2 = 250;

draw = () => {
  ctx.fillStyle = "#b2c7ef";
  ctx.fillRect(0, 0, 800, 800); 

  ctx.fillStyle = "#ffffff";

  drawCircle(circleX, circleY, circleR);
  drawLine(lineX1, lineY1, lineX2, lineY2);
}

console.log(lineSegmentIntersectsCircle(lineX1, lineY1, lineX2, lineY2, circleX, circleY, circleR))

draw();
canvas { display: flex; margin: 0 auto; }
<canvas width="400" height="400"></canvas>

我认为(1)计算线盘交点会更简单,它可以是空的、点或线段(2)测试交点是否与线段相交。

直线上的点是((1-t) x1 + t x2, (1-t) y1 + t y2)对于所有真实的t. Let x(t) = (1-t) x1 + t x2 - cx and y(t) = (1-t) y1 + t y2 - cy。线盘交点非空当且仅当多项式x(t)^2 + y(t)^2 - r^2 = 0有真正的根源t1 <= t2。在这种情况下,线盘交点全部为t in [t1, t2]。线段都是t in [0, 1]。两者相交当且仅当t1 <= 1 and t2 >= 0.

计算t1 and t2需要平方根,我们可以避免。让a, b, c是这样的x(t)^2 + y(t)^2 - r^2 = a t^2 + b t + c。我们有t1 + t2 = -b/a and t1 t2 = c/a.

  • t1 and t2是真实的当且仅当b^2 - 4 a c >= 0.

  • 条件t1 <= 1为假当且仅当t1 - 1 > 0 and t2 - 1 > 0,当且仅当(t1 - 1) + (t2 - 1) > 0 and (t1 - 1) (t2 - 1) > 0,这相当于-b/a - 2 > 0 and c/a + b/a + 1 > 0. Since a > 0,这简化为-b > 2 a and c + b + a > 0.

  • 条件t2 >= 0为假当且仅当t1 < 0 and t2 < 0,当且仅当t1 + t2 = -b/a < 0 and t1 t2 = c/a > 0. Since a > 0,这简化为b > 0 and c > 0.

在 JavaScript 中实现。

function lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r) {
  let x_linear = x2 - x1;
  let x_constant = x1 - cx;
  let y_linear = y2 - y1;
  let y_constant = y1 - cy;
  let a = x_linear * x_linear + y_linear * y_linear;
  let half_b = x_linear * x_constant + y_linear * y_constant;
  let c = x_constant * x_constant + y_constant * y_constant - r * r;
  return (
    half_b * half_b >= a * c &&
    (-half_b <= a || c + half_b + half_b + a <= 0) &&
    (half_b <= 0 || c <= 0)
  );
}

function lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) {
  let deltaX = x2 - x1;
  let deltaY = y2 - y1;

  let mag = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

  let unitX = deltaX / mag;
  let unitY = deltaY / mag;

  let d = (cx - x1) * unitY - (cy - y1) * unitX;

  if (d < -r || d > r) {
    return false;
  }

  let x1CXDelta = x1 - cx;
  let y1CYDelta = y1 - cy;

  let x2CXDelta = x2 - cx;
  let y2CYDelta = y2 - cy;

  let pointOneWithinCircle =
    x1CXDelta * x1CXDelta + y1CYDelta * y1CYDelta < r * r;
  if (pointOneWithinCircle) {
    return true;
  }

  let pointTwoWithinCircle =
    x2CXDelta * x2CXDelta + y2CYDelta * y2CYDelta < r * r;
  if (pointTwoWithinCircle) {
    return true;
  }

  let foo = unitX * x1 + unitY * y1;
  let bar = unitX * cx + unitY * cy;
  let baz = unitX * x2 + unitY * y2;

  return (foo < bar && bar < baz) || (baz < bar && bar < foo);
}

function test() {
  for (let i = 0; i < 10000000; i++) {
    let x1 = Math.random();
    let y1 = Math.random();
    let x2 = Math.random();
    let y2 = Math.random();
    let cx = Math.random();
    let cy = Math.random();
    let r = Math.random();
    if (
      lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) !=
      lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r)
    ) {
      console.log("bad");
      break;
    }
  }
}

test();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这段用于确定圆和线段是否相交的代码正确吗? 的相关文章

  • 类似于 iPhone(老虎机)的网络“选择器”选择框? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个类似 iPhone 的 选择器 控件 我可以在网络上使用它 可访问性不是问题 JavaS
  • 构建基于纯 JavaScript 的 Web 应用程序(客户端和服务器端)是否有意义? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我一直认为 JavaScript 是任何 Web 应用程序客户端的一个很好的补充 或者更确切地说 在过去几年中 是一个必须具备的功能 即使当我开
  • 我可以使用 javascript 捕获并保存网页的当前状态吗

    我需要使用 javascript 获取页面的全部内容并将其发送到服务器脚本以保存它 我想在用户使用 AJAX 和其他 javascript 工具对页面进行一些更改后执行此操作 我不想要某些元素的状态 我想基本上获取 body 标记内的所有内
  • 我在 firebase.auth.ApplicationVerifier 中遇到问题

    错误发生在signInWithPhoneNumber 的第二个参数中 我无法解决这个问题 我使用了三种方法来发送 otp 验证 otp 和最后用于验证码 methods sendOTP e e preventDefault if this
  • Moment.js 在 Firefox 中返回 NaN,但在 Chrome 中不返回 NaN

    我有以下代码行 moment 11 10 2013 09 03 AM diff moment minutes 在 Chrome 30 0 1599 101 中 以下行返回一个数字 它每分钟都会更改 因此确切的值不相关 在 Firefox 2
  • 转义双引号 JavaScript

    我试图在 iPhone 上查看时运行某种图像格式 在其他情况下运行一些 Flash 视频 var uagent navigator userAgent toLowerCase if uagent search iphone gt 1 doc
  • 计算链接上的点击次数(不带 onclick)[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有诸如此类的链接 a href h
  • 在 VueJs 中使用上下键自动完成搜索

    除了自动完成搜索之外 我还想添加功能以允许使用 VueJs 按下 向上键功能 我的模板如下所示 div h2 Todos h2 div class autocomplete div div
  • 将数组传递给 include() javascript

    我试图找出一个字符串是否包含存储在数组中的多个字符串 includes 所以我尝试过 let string hello james console log string includes hello james 但它被返回为false 当我
  • angular.isdefine 有什么好处?

    有什么好处angular isdefined超过和超过foo undefined 我一时想不出有什么好处 在 Javascript 中以任何方式访问真正未定义的变量 除了 typeof 都会抛出错误 你只能使用Angular isDefin
  • 使用 javascript onClick 播放 mp3 文件

    我正在播放 mp3 文件 只是 javascript onClick 下面是我的代码 Music File 1
  • 强制 Javascript 编码风格的工具[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我需要自动检查不同人编写的javascript源代码的风格 你知道有什么好的工具可以做到这一点吗 与 emacs 集成将是一个优势 先感谢
  • 如何将对象传递给 onclick 事件[重复]

    这个问题在这里已经有答案了 可能的重复 Javascript 循环内的事件处理程序 需要闭包吗 https stackoverflow com questions 341723 event handlers inside a javascr
  • 在模型对象上设置属性?

    Hi 我正在构建 ASP NET MVC 站点并遇到了问题 在我的项目中 我得到了一个 modelview 类 其中包含几个属性 例如 public class myModelView public int MyProperty1 get
  • CSS Hex 到速记十六进制转换

    将十六进制转换为速记十六进制的正确算法是什么 例如 996633很容易被转换为 963 但如果是这样怎么办 F362C3 我的第一个猜测是我只取每种颜色的第一个值并使用它 所以 F362C3变成 F6C 但我不知道如何从数学上证明这种方法的
  • 是否可以模拟 isTrusted=true

    我希望在调用 touchStart 事件时能够模拟 isTrusted true 是否有任何库或任何类型的解决方法可以实现这一点 以下是我以编程方式运行 touchStart 时的输出与实际调用 touchStart 时的输出 我正在使用移
  • 如何暂时停止标题属性显示工具提示?

    我在右键单击时显示了一个弹出 div 我知道这会破坏预期的功能 但 Google 文档会这样做 所以为什么不呢 但是 我在弹出窗口上显示的元素有一个 标题 属性集 该属性集出现在我的分区 我仍然希望工具提示能够工作 但当弹出窗口出现时就不行
  • 防止IndexedDB请求错误取消事务

    我的意图 循环localStorage并将数据放入IndexedDB 如果发生某些已知错误 例如当键已存在时出现 ConstraintError 我想忽略这些特定错误 以便事务不会中止 当请求触发错误时 中止事务是默认行为 问题 我以为使用
  • Firebug 分析问题:“没有要分析的活动”

    我想用一些 javascript jQuery 尝试一些不同的选项 看看哪个是最快的 但是我无法让分析正常工作 这是我要测试的代码 this keypress function e console profile test retrieve
  • Java 将函数添加到 json 对象而不使用引号。

    我正在用 java 构建一个 json 对象 我需要将一个函数传递到我的 javascript 中并使用 jquery isFunction 对其进行验证 我遇到的问题是我必须将 json 对象中的函数设置为字符串 但 json 对象将周围

随机推荐