【问题标题】:calculating the point of intersection of two lines计算两条线的交点
【发布时间】:2012-12-18 16:54:15
【问题描述】:

我有动态生成动画的线条,我想检测线条何时撞到另一条线条。我正在尝试实现一些基本的线性代数来获得线的方程,然后求解 x,y,但结果不稳定。在这一点上,我只用两条线进行测试,这意味着我应该得到一个交点,但我得到了两个。我只是想确保我的数学没问题,我应该在别处寻找问题。

function collision(boid1, boid2) {
  var x1 = boid1.initialX, y1 = boid1.initialY, x2 = boid1.x, y2 = boid1.y, x3 = boid2.initialX, y3 = boid2.initialY, x4 = boid2.x, y4 = boid2.y;
  slope1 = (y1 - y2)/(x1 - x2);
  slope2 = (y3 - y4)/(x3- x4);

  if(slope1 != slope2){
    var b1 = getB(slope1,x1,y1);
    var b2 = getB(slope2,x3,y3);

    if(slope2 >= 0){
      u = slope1 - slope2;
    }else{
      u = slope1 + slope2;
    }

    if(b1 >= 0){
      z = b2 - b1;
    }else{
      z = b2 + b1;
    }
    pointX = z / u;
    pointY = (slope1*pointX)+b1;
    pointYOther = (slope2*pointX)+b2;
    console.log("pointx:"+pointX+" pointy:"+pointY+" othery:"+pointYOther);
    context.beginPath();
    context.arc(pointX, pointY, 2, 0, 2 * Math.PI, false);
    context.fillStyle = 'green';
    context.fill();
    context.lineWidth = 1;
    context.strokeStyle = '#003300';
    context.stroke();
  }
  return false;
}

function getB(slope,x,y){
  var y = y, x = x, m = slope;
  a = m*x;
  if(a>=0){
    b = y - a;
  }else{
    b = y + a;
  }
  return b;
}

问题是我得到了两个不同的交点值。应该只有一个,这让我相信我的计算是错误的。是的,x2,y2,x4,y4 都在移动,但它们有一个固定的角​​度,一致的坡度证实了这一点。

【问题讨论】:

  • 请添加一个short self-contained example,它显示了问题。目前,您的帖子既缺少问题又缺少问题,它应该至少包含其中一个。 (注意:您告诉我们结果不稳定,但您没有告诉我们问题的确切性质)。
  • 你不断检查事情是否积极的事实是一个危险信号。这不应该是必要的。此外,您绝对没有处理其中一条线是垂直的情况(即slope = Infinity)。
  • Aaron,我的做法是像人类试图解决变量一样。什么是更有效的方法?

标签: javascript linear-algebra intersection


【解决方案1】:

我找到了great solution by Paul Bourke。在这里,用 JavaScript 实现:

function line_intersect(x1, y1, x2, y2, x3, y3, x4, y4)
{
    var ua, ub, denom = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1);
    if (denom == 0) {
        return null;
    }
    ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3))/denom;
    ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3))/denom;
    return {
        x: x1 + ua * (x2 - x1),
        y: y1 + ua * (y2 - y1),
        seg1: ua >= 0 && ua <= 1,
        seg2: ub >= 0 && ub <= 1
    };
}

【讨论】:

  • 请注意,此解决方案中有一个拼写错误:seg2 的计算应显示“ub >= 0 && ub = 0 && ua
  • 我对 Paul 网站上的 javascript 实现的运气更好:paulbourke.net/geometry/pointlineplane/javascript.txt
  • 我不知道为什么,但是在这个解决方案中,交点总是有一个 y 偏移量。上面的链接完美无缺。
  • @musemind 你能举个例子吗?
  • 我也得到了 y 偏移量。请注意,在上面的答案中(就像现在一样),y 位置是使用“y1 + ub * (y2 - y1)”计算的,而 Paul Bourke 在他的网站上使用“y1 + ua * (y2 - y1)”(注意ua 与 ub 的区别)。 Paul 对 x 和 y 都使用 ua。如果我使用行 {x1:100, y1:0, x2:100, y2:218, x3:0, y3:100, x4:200, y4:100} 上面的答案给出 100X109,而 Bourke 的代码给出 100X100 (这是正确的)
【解决方案2】:

对于线段-线段交点,使用Paul Bourke's solution:

// line intercept math by Paul Bourke http://paulbourke.net/geometry/pointlineplane/
// Determine the intersection point of two line segments
// Return FALSE if the lines don't intersect
function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {

  // Check if none of the lines are of length 0
    if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) {
        return false
    }

    denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))

  // Lines are parallel
    if (denominator === 0) {
        return false
    }

    let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator
    let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator

  // is the intersection along the segments
    if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
        return false
    }

  // Return a object with the x and y coordinates of the intersection
    let x = x1 + ua * (x2 - x1)
    let y = y1 + ua * (y2 - y1)

    return {x, y}
}

对于无限的线交点,使用Justin C. Round's algorithm:

function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
    // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
    var denominator, a, b, numerator1, numerator2, result = {
        x: null,
        y: null,
        onLine1: false,
        onLine2: false
    };
    denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
    if (denominator == 0) {
        return result;
    }
    a = line1StartY - line2StartY;
    b = line1StartX - line2StartX;
    numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
    numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
    a = numerator1 / denominator;
    b = numerator2 / denominator;

    // if we cast these lines infinitely in both directions, they intersect here:
    result.x = line1StartX + (a * (line1EndX - line1StartX));
    result.y = line1StartY + (a * (line1EndY - line1StartY));

    // if line1 is a segment and line2 is infinite, they intersect if:
    if (a > 0 && a < 1) {
        result.onLine1 = true;
    }
    // if line2 is a segment and line1 is infinite, they intersect if:
    if (b > 0 && b < 1) {
        result.onLine2 = true;
    }
    // if line1 and line2 are segments, they intersect if both of the above are true
    return result;
};

【讨论】:

    【解决方案3】:

    将 'found-x' 插回方程式之一时,您无需在加/减 y 相交之间交替:

    (function () {
        window.linear = {
            slope: function (x1, y1, x2, y2) {
                if (x1 == x2) return false;
                return (y1 - y2) / (x1 - x2);
            },
            yInt: function (x1, y1, x2, y2) {
                if (x1 === x2) return y1 === 0 ? 0 : false;
                if (y1 === y2) return y1;
                return y1 - this.slope(x1, y1, x2, y2) * x1 ;
            },
            getXInt: function (x1, y1, x2, y2) {
                var slope;
                if (y1 === y2) return x1 == 0 ? 0 : false;
                if (x1 === x2) return x1;
                return (-1 * ((slope = this.slope(x1, y1, x2, y2)) * x1 - y1)) / slope;
            },
            getIntersection: function (x11, y11, x12, y12, x21, y21, x22, y22) {
                var slope1, slope2, yint1, yint2, intx, inty;
                if (x11 == x21 && y11 == y21) return [x11, y11];
                if (x12 == x22 && y12 == y22) return [x12, y22];
    
                slope1 = this.slope(x11, y11, x12, y12);
                slope2 = this.slope(x21, y21, x22, y22);
                if (slope1 === slope2) return false;
    
                yint1 = this.yInt(x11, y11, x12, y12);
                yint2 = this.yInt(x21, y21, x22, y22);
                if (yint1 === yint2) return yint1 === false ? false : [0, yint1];
    
                if (slope1 === false) return [y21, slope2 * y21 + yint2];
                if (slope2 === false) return [y11, slope1 * y11 + yint1];
                intx = (slope1 * x11 + yint1 - yint2)/ slope2;
                return [intx, slope1 * intx + yint1];
            }
        }
    }());
    

    【讨论】:

    • 谢谢。此处为“line2.yint = line2.slope * line2.x2 - line1.y1;”你的意思是“line2.yint = line2.slope * line2.x2 - line2.y2;”?
    • @Adam,修复它,我的意思是line2.x1
    • 为了解决我们不需要位于特定线上的任何点吗?你能解释一下为什么你可以使用 line1.y1 来求解第 2 行的 y-int 吗?
    • 再次失控。事实上,我确实需要 line2.x1line2.y1 来获取 line2 的 yint。固定。
    • 它是否计算正确的 y-int?我认为它得到了正确的数字,但将其切换为相反的数字;正变负,负变正。
    【解决方案4】:

    你可以这样做;

    function lineIntersect(a,b){
      a.m = (a[0].y-a[1].y)/(a[0].x-a[1].x);  // slope of line 1
      b.m = (b[0].y-b[1].y)/(b[0].x-b[1].x);  // slope of line 2
      return a.m - b.m < Number.EPSILON ? undefined
                                        : { x: (a.m * a[0].x - b.m*b[0].x + b[0].y - a[0].y) / (a.m - b.m),
                                            y: (a.m*b.m*(b[0].x-a[0].x) + b.m*a[0].y - a.m*b[0].y) / (b.m - a.m)};
    }
    
    var line1 = [{x:3, y:3},{x:17, y:8}],
        line2 = [{x:7, y:10},{x:11, y:2}];
    console.log(lineIntersect(line1, line2));

    【讨论】:

    • 这是一个简洁明了的答案,但由于它将线视为(斜率)方程,因此无法确定相交是发生在线段内还是线段外。跨度>
    • 为了快速解决问题,我首先使用stackoverflow.com/a/28866825/197546 此处的函数测试了它是否相交,并且仅在真正计算实际位置时才进行测试,但我确信有一个更有效的解决方案
    • @Daniel Hmm.. 是的,你是对的,但是一旦你知道它们的交点,测试交点是否在线段之一的 Epsilon 附近是相当简单的。但是,是的,可能有更有效的方法。一如既往。
    【解决方案5】:

    有一个 npm 模块可以做到这一点:line-intersect

    使用安装它

    npm install --save line-intersect
    

    ES6 用法:

    import { checkIntersection } from "line-intersect";
    
    const result = lineIntersect.checkIntersection(
      line1.start.x, line1.start.y, line1.end.x, line1.end.y,
      line2.start.x, line2.start.y, line2.end.x, line2.end.y
    );
    
    result.type  // any of "none", "parallel", "colinear", "intersecting"
    result.point // only exists when result.type == 'intersecting'
    

    如果您使用的是打字稿,这里是打字:

    declare module "line-intersect" {
      export function checkIntersection(
        x1: number, y1: number,
        x2: number, y2: number,
        x3: number, y3: number,
        x4: number, y4: number): {
            type: string,
            point: {x:number, y:number}
        }; 
    }
    

    把它放在一个文件中,如果在tsconfig.json"files"部分中引用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-07
      • 1970-01-01
      • 2011-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多