JavaScript 教程:检测线段与圆的相交

javascript 教程:检测线段与圆的相交

本教程详细介绍了如何使用 JavaScript 检测线段与圆是否相交。通过避免使用平方根运算,提供了一种高效的相交检测方法。同时,还提供了一个进阶函数,用于计算线段与圆的交点距离,并附带完整的代码示例和演示,帮助开发者理解和应用这些技术。

在 HTML5 Canvas 游戏中,碰撞检测是至关重要的一个环节。本教程将探讨如何使用 JavaScript 来判断一条线段是否与一个圆相交,并提供优化后的代码示例,避免了昂贵的平方根运算,提升性能。

线段与圆相交的判断

核心思想是计算圆心到线段的距离,并与圆的半径进行比较。如果圆心到线段的距离小于圆的半径,则线段与圆相交。为了优化性能,我们避免使用 Math.sqrt() 函数,而是直接比较距离的平方。

以下是一个用于检测线段与圆是否相交的 JavaScript 函数:

立即学习“Java免费学习笔记(深入)”;

function rayInterceptsCircle(ray, circle) {  const dx = ray.p2.x - ray.p1.x;  const dy = ray.p2.y - ray.p1.y;  const u = Math.min(1, Math.max(0, ((circle.x - ray.p1.x) * dx + (circle.y - ray.p1.y) * dy) / (dy * dy + dx * dx)));  const nx = ray.p1.x + dx * u - circle.x;  const ny = ray.p1.y + dy * u - circle.y;  return nx * nx + ny * ny < circle.radius * circle.radius;}

代码解释:

ray: 包含线段端点 p1 和 p2 的对象,例如:{ p1: {x: 0, y: 50}, p2: {x: 300, y: 50} }circle: 包含圆心坐标 x、y 和半径 radius 的对象,例如:{ x: 150, y: 120, radius: 60 }dx 和 dy: 线段的 x 和 y 方向的差值。u: 一个参数,表示圆心投影到线段上的位置,范围在 0 到 1 之间。Math.min(1, Math.max(0, …)) 确保 u 的值在 0 和 1 之间,这意味着我们只考虑线段上的点,而不是线段的延长线。nx 和 ny: 圆心到线段上最近点的向量。*`nx nx + ny ny circle.radius**: 判断圆心到线段的距离的平方是否小于圆半径的平方。避免了使用Math.sqrt()` 函数,提高了性能。

计算线段与圆的交点距离 (进阶)

如果你需要知道线段与圆的交点距离,可以使用以下函数:

function rayDist2Circle(ray, circle) {  const dx = ray.p2.x - ray.p1.x;  const dy = ray.p2.y - ray.p1.y;  const vcx = ray.p1.x - circle.x;  const vcy = ray.p1.y - circle.y;  var v = (vcx * dx + vcy * dy) * (-2 / Math.hypot(dx, dy));  const dd = v * v - 4 * (vcx * vcx + vcy * vcy - circle.radius * circle.radius);  if (dd <= 0) {    return Infinity;  }  return (v - Math.sqrt(dd)) / 2;}

代码解释:

如果线段没有与圆相交,函数返回 Infinity。如果线段与圆相交,函数返回线段的起点到交点的距离。Math.hypot(dx, dy) 计算线段的长度,避免了平方根运算。

完整示例代码

  Line Circle Intersection      canvas {      position: absolute;      top: 0px;      left: 0px;    }          const canvas = document.getElementById("canvas");    const ctx = canvas.getContext("2d");    const TAU = Math.PI * 2;    requestAnimationFrame(renderLoop);    var W = canvas.width, H = canvas.height;    const Point = (x, y) => ({x, y});    const Ray = (p1, p2) => ({p1, p2});    const Circle = (p, radius) => ({x: p.x, y: p.y, radius});    function drawRayLeng(ray, len) {      ctx.beginPath();      ctx.lineTo(ray.p1.x, ray.p1.y);      if (len < Infinity) {        const dx = ray.p2.x - ray.p1.x;        const dy = ray.p2.y - ray.p1.y;        const scale = len / Math.hypot(dx, dy);        ctx.lineTo(ray.p1.x + dx * scale , ray.p1.y + dy  * scale);      } else {        ctx.lineTo(ray.p2.x, ray.p2.y);      }      ctx.stroke();    }    function drawRay(ray) {      ctx.beginPath();      ctx.lineTo(ray.p1.x, ray.p1.y);      ctx.lineTo(ray.p2.x, ray.p2.y);      ctx.stroke();    }    function drawCircle(circle) {      ctx.beginPath();      ctx.arc(circle.x, circle.y, circle.radius, 0, TAU);      ctx.stroke();    }    function rayInterceptsCircle(ray, circle) {        const dx = ray.p2.x - ray.p1.x;        const dy = ray.p2.y - ray.p1.y;        const u = Math.min(1, Math.max(0, ((circle.x - ray.p1.x) * dx + (circle.y - ray.p1.y) * dy) / (dy * dy + dx * dx)));        const nx = ray.p1.x + dx * u - circle.x;        const ny = ray.p1.y + dy * u - circle.y;        return nx * nx + ny * ny < circle.radius * circle.radius;    }    function rayDist2Circle(ray, circle) {        const dx = ray.p2.x - ray.p1.x;        const dy = ray.p2.y - ray.p1.y;        const vcx = ray.p1.x - circle.x;        const vcy = ray.p1.y - circle.y;        var v =  (vcx * dx +  vcy * dy) * (-2 / Math.hypot(dx, dy));        const dd = v * v - 4 * (vcx * vcx + vcy * vcy - circle.radius * circle.radius);        if (dd <= 0) { return Infinity; }        return  (v - Math.sqrt(dd)) / 2;    }    const mouse  = {x : 0, y : 0}    function mouseEvents(e){        mouse.x = e.pageX;        mouse.y = e.pageY;    }    document.addEventListener("mousemove", mouseEvents);    const c1 = Circle(Point(150, 120), 60);    const r1 = Ray(Point(0, 50), Point(300, 50));    function renderLoop(time) {       ctx.clearRect(0, 0, W, H);       r1.p1.x = c1.x + Math.cos(time / 5000) * 100;       r1.p1.y = c1.y + Math.sin(time / 5000) * 100;       r1.p2.x = mouse.x;       r1.p2.y = mouse.y;       ctx.lineWidth = 0.5;       drawCircle(c1);       drawRay(r1);       ctx.lineWidth = 5;       if (rayInterceptsCircle(r1, c1)) {         ctx.strokeStyle = "red";         drawRayLeng(r1, rayDist2Circle(r1, c1));       } else {         drawRay(r1);       }       ctx.strokeStyle = "black";       requestAnimationFrame(renderLoop);    }  

使用方法:

将以上代码复制到 HTML 文件中。使用浏览器打开该 HTML 文件。移动鼠标,线段的终点会跟随鼠标移动。如果线段与圆相交,线段会变成红色,并且只绘制到交点处。

总结

本教程提供了一种高效且易于理解的 JavaScript 方法来检测线段与圆的相交。通过避免使用平方根运算,提高了性能,尤其是在需要进行大量碰撞检测的场景下。同时,还介绍了如何计算线段与圆的交点距离,为更复杂的碰撞处理提供了基础。在实际应用中,可以根据具体需求选择使用哪个函数,并进行适当的调整和优化。

以上就是JavaScript 教程:检测线段与圆的相交的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1510193.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
ES6中如何用正则表达式的d标志获取索引
上一篇 2025年12月20日 05:49:41
ES6的导出别名如何重命名模块
下一篇 2025年12月20日 05:49:53

相关推荐

发表回复

登录后才能评论
关注微信