如何检查一个点是否位于其他2个点之间的一条线上

时间:2012-08-10 19:20:13

标签: algorithm geometry

我该怎么写这个功能?任何赞赏的例子

function isPointBetweenPoints(currPoint, point1, point2):Boolean {

    var currX = currPoint.x;
    var currY = currPoint.y;

    var p1X = point1.x;
    var p1y = point1.y;

    var p2X = point2.x;
    var p2y = point2.y;

    //here I'm stuck
}

7 个答案:

答案 0 :(得分:41)

假设point1point2不同,首先检查点是否在线上。为此,您只需要向量point1 -> currPointpoint1 -> point2的“交叉产品”。

dxc = currPoint.x - point1.x;
dyc = currPoint.y - point1.y;

dxl = point2.x - point1.x;
dyl = point2.y - point1.y;

cross = dxc * dyl - dyc * dxl;

当且仅当cross等于零时,你的观点就在这一行上。

if (cross != 0)
  return false;

现在,正如您所知道的那一点确实存在于线上,现在是时候检查是否位于原始点之间。这可以通过比较x坐标,如果线条“比水平线更垂直”或“y坐标”

轻松完成
if (abs(dxl) >= abs(dyl))
  return dxl > 0 ? 
    point1.x <= currPoint.x && currPoint.x <= point2.x :
    point2.x <= currPoint.x && currPoint.x <= point1.x;
else
  return dyl > 0 ? 
    point1.y <= currPoint.y && currPoint.y <= point2.y :
    point2.y <= currPoint.y && currPoint.y <= point1.y;

注意,如果输入数据是整数,则上述算法如果完全是整数,即它不需要整数输入的浮点计算。在计算cross时要注意潜在的溢出。

P.S。该算法绝对精确,这意味着它将拒绝非常接近线但不精确在线上的点。有时这不是我们需要的。但那是一个不同的故事。

答案 1 :(得分:21)

Distance(point1,currPoint)+Distance(currPoint,point2)==Distance(point1,point2)

但是如果你有浮点值,请注意,它们的不同之处......

答案 2 :(得分:4)

您想检查从point1currPoint的斜率是否与从currPointpoint2的斜率相同,所以:

m1 = (currY - p1Y) / (currX - p1X);
m2 = (p2Y - currY) / (p2X - currX);

您还想检查currPoint是否在其他两个人创建的框内,所以:

return (m1 == m2) && (p1Y <= currY && currY <= p2Y) && (p1X <= currX && currX <= p2X);

编辑:这不是一个很好的方法;看看maxim1000's solution是否有更正确的方法。

答案 3 :(得分:3)

这与Javascript无关。尝试以下算法,点p1 = point1和p2 = point2,第三点是p3 = currPoint:

v1 = p2 - p1
v2 = p3 - p1
v3 = p3 - p2
if (dot(v2,v1)>0 and dot(v3,v1)<0) return between
else return not between

如果你想确定它位于p1和p2之间的线段上:

v1 = normalize(p2 - p1)
v2 = normalize(p3 - p1)
v3 = p3 - p2
if (fabs(dot(v2,v1)-1.0)<EPS and dot(v3,v1)<0) return between
else return not between

答案 4 :(得分:1)

准备好接受似乎比其他一些解决方案简单得多的方法了吗?

您向它传递三个点(三个具有 x 和 y 属性的对象)。点 1 和点 2 定义您的线,点 3 是您要测试的点。

function pointOnLine(pt1, pt2, pt3) {
    const dx = (pt3.x - pt1.x) / (pt2.x - pt1.x);
    const dy = (pt3.y - pt1.y) / (pt2.y - pt1.y);
    const onLine = dx === dy

    // Check on or within x and y bounds
    const betweenX = 0 <= dx && dx <= 1;
    const betweenY = 0 <= dy && dy <= 1;

    return onLine && betweenX && betweenY;
}

console.log('pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2})');
console.log(pointOnLine({ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }));

console.log('pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 0.5, y: 0.5})');
console.log(pointOnLine({ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 0.5, y: 0.5 }));

编辑:根据 RBarryYoung 的观察进一步简化。

答案 5 :(得分:0)

Graphic illustration

如果要查找其他点是否位于其他两个点之间的直线上,则需要找到这些点的向量并计算这些向量的点积。

假设您有两个点A和B。还有要测试的点P。首先,您要计算向量PA,PB

  • PA = A-P
  • PB = B-P

然后计算那些向量DotProduct(PA,PB)的点积。为简化起见,我们假设计算是在2D模式下进行的。

test_dot_product = DotProduct(PA, PB) = PA.x * PB.x + PA.y * PB.y

因此,当计算点积时,我们需要检查其值是否小于或等于0。如果是,则意味着向量PA和PB朝向不同的方向,而点P位于A和B之间。否则,向量朝向相同的方向和点P在AB范围之外。

if(test_dot_product <= 0.0f){
    Point P IS between A and B
}
else{
    Point P IS NOT between A and B
}

这是一些示例代码。那是我引擎的2D物理光线投射系统的代码。当我找到射线的命中点和边缘时,我需要检查它是否在边缘的最小和最大点之间...

    ....
    v2 HitPP1 = Edge->Min - HitData->HitPoint;
    v2 HitPP2 = Edge->Max - HitData->HitPoint;

    //NOTE(dima): If hit point is between edge min and max points
    if (Dot(HitPP1, HitPP2) <= 0.0f) {
        HitData->HitHappened = 1;
        AtLeastOneHitHappened = 1;
    }
    .....

对不起,我的英语水平

答案 6 :(得分:0)

我将使用Triangle方法: Triangle approach

首先,我将检查区域,如果区域接近0,则该点位于直线上。

但是考虑一下AC的长度如此之大,然后Area远离0增长的情况,但是在视觉上,我们仍然看到B在AC上:当我们需要检查三角形的高度时。< / p>

为此,我们需要记住从一年级学习的公式:Area = Base * Height / 2

代码如下:

    bool Is3PointOn1Line(IList<Vector2> arrVert, int idx1, int idx2, int idx3)
    {
        //check if the area of the ABC triangle is 0:
        float fArea = arrVert[idx1].x * (arrVert[idx2].y - arrVert[idx3].y) +
            arrVert[idx2].x * (arrVert[idx3].y - arrVert[idx1].y) +
            arrVert[idx3].x * (arrVert[idx1].y - arrVert[idx2].y);
        fArea = Mathf.Abs(fArea);
        if (fArea < SS.EPSILON)
        {
            //Area is zero then it's the line
            return true;
        }
        else
        {
            //Check the height, in case the triangle has long base
            float fBase = Vector2.Distance(arrVert[idx1], arrVert[idx3]);
            float height = 2.0f * fArea / fBase;
            return height < SS.EPSILON;
        }
    }

用法:

Vector2[] arrVert = new Vector2[3];

arrVert[0] = //...
arrVert[1] = //...
arrVert[2] = //...

if(Is3PointOn1Line(arrVert, 0, 1, 2))
{
    //Ta-da, they're on same line
}

PS:SS.EPSILON = 0.01f,我使用了Unity的某些功能(例如Vector2.Distance),但您明白了。