找到一个点是否位于矩形内

时间:2010-05-02 07:08:16

标签: algorithm geometry

我想找出一个点是否位于矩形内部。矩形可以任何方式定向,不需要轴对齐。

我能想到的一种方法是旋转矩形和点坐标以使矩形轴对齐,然后通过简单地测试点的坐标是否位于矩形的坐标内。

上述方法需要旋转,因此需要浮点运算。还有其他有效的方法吗?

10 个答案:

答案 0 :(得分:75)

矩形是如何表示的?三点?四点?点,侧面和角度?两点一面?别的什么?在不知情的情况下,任何回答你问题的尝试都只具有纯粹的学术价值。

在任何情况下,对于任何多边形(包括矩形),测试都非常简单:检查多边形的每个边缘,假设每个边缘都是逆时针方向,并测试点是否位于到边缘的左侧(在左半平面中)。如果所有边都通过测试 - 该点在内部。如果至少有一个失败 - 这一点在外面。

为了测试点(xp, yp)是否位于边(x1, y1) - (x2, y2)的左侧,您只需要计算

D = (x2 - x1) * (yp - y1) - (xp - x1) * (y2 - y1)

如果D > 0,则该点位于左侧。如果D < 0,则该点位于右侧。如果D = 0,则该点就在该行上。


此答案的先前版本描述了左侧测试的看似不同的版本(见下文)。但可以很容易地证明它计算的值相同。

...为了测试点(xp, yp)是否位于边(x1, y1) - (x2, y2)的左侧,您需要为包含边的线构建线方程。公式如下

A * x + B * y + C = 0

其中

A = -(y2 - y1)
B = x2 - x1
C = -(A * x1 + B * y1)

现在你需要做的就是计算

D = A * xp + B * yp + C

如果D > 0,则该点位于左侧。如果D < 0,则该点位于右侧。如果D = 0,则该点就在该行上。

但是,此测试同样适用于任何凸多边形,这意味着它对于矩形可能过于通用。矩形可能允许更简单的测试...例如,在矩形(或任何其他平行四边形)中,AB的值具有相同的幅度但不同的符号(即平行)边缘,可以用来简化测试。

答案 1 :(得分:38)

假设矩形由三个点A,B,C表示,AB和BC垂直,您只需要检查AB和BC上查询点M的投影:

0 <= dot(AB,AM) <= dot(AB,AB) &&
0 <= dot(BC,BM) <= dot(BC,BC)

AB是向量AB,坐标(Bx-Ax,By-Ay),dot(U,V)是向量U和V的点积:Ux*Vx+Uy*Vy

<强>更新即可。我们举一个例子来说明这一点:A(5,0)B(0,2)C(1,5)和D(6,3)。 从点坐标,我们得到AB =( - 5,2),BC =(1,3),点(AB,AB)= 29,点(BC,BC)= 10.

对于查询点M(4,2),我们有AM =( - 1,2),BM =(4,0),点(AB,AM)= 9,点(BC,BM)= 4。 M在矩形内。

对于查询点P(6,1),我们有AP =(1,1),BP =(6,-1),点(AB,AP)= - 3,点(BC,BP)= 3 。 P不在矩形内,因为它在AB侧的投影不在AB段内。

答案 2 :(得分:17)

我借用了Eric Bainville的回答:

0 <= dot(AB,AM) <= dot(AB,AB) && 0 <= dot(BC,BM) <= dot(BC,BC)

在javascript中看起来像这样:

function pointInRectangle(m, r) {
    var AB = vector(r.A, r.B);
    var AM = vector(r.A, m);
    var BC = vector(r.B, r.C);
    var BM = vector(r.B, m);
    var dotABAM = dot(AB, AM);
    var dotABAB = dot(AB, AB);
    var dotBCBM = dot(BC, BM);
    var dotBCBC = dot(BC, BC);
    return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC;
}

function vector(p1, p2) {
    return {
            x: (p2.x - p1.x),
            y: (p2.y - p1.y)
    };
}

function dot(u, v) {
    return u.x * v.x + u.y * v.y; 
}

例如:

var r = {
    A: {x: 50, y: 0},
    B: {x: 0, y: 20},
    C: {x: 10, y: 50},
    D: {x: 60, y: 30}
};

var m = {x: 40, y: 20};

然后:

pointInRectangle(m, r); // returns true.

这是一个将输出绘制为可视化测试的codepen :) http://codepen.io/mattburns/pen/jrrprN

enter image description here

答案 3 :(得分:15)

# Pseudo code
# Corners in ax,ay,bx,by,dx,dy
# Point in x, y

bax = bx - ax
bay = by - ay
dax = dx - ax
day = dy - ay

if ((x - ax) * bax + (y - ay) * bay < 0.0) return false
if ((x - bx) * bax + (y - by) * bay > 0.0) return false
if ((x - ax) * dax + (y - ay) * day < 0.0) return false
if ((x - dx) * dax + (y - dy) * day > 0.0) return false

return true

答案 4 :(得分:12)

我意识到这是一个老话题,但对于那些从纯数学角度看待这个问题的人来说,数学堆栈交换中有一个很好的线程,在这里:

https://math.stackexchange.com/questions/190111/how-to-check-if-a-point-is-inside-a-rectangle

编辑:受这个主题的启发,我已经整理了一个简单的矢量方法,可以快速确定你的观点所在。

假设你有一个矩形,其点在p1 =(x1,y1),p2 =(x2,y2),p3 =(x3,y3),p4 =(x4,y4),顺时针方向。如果点p =(x,y)位于矩形内,则点积(p-p1)。(p2-p1)将位于0和| p2-p1 | ^ 2和(p-p1)之间。 (p4 - p1)将介于0和| p4 - p1 | ^ 2之间。这相当于沿着矩形的长度和宽度投影向量p - p1,以p1为原点。

如果我显示一个等效的代码,这可能更有意义:

p21 = (x2 - x1, y2 - y1)
p41 = (x4 - x1, y4 - y1)

p21magnitude_squared = p21[0]^2 + p21[1]^2
p41magnitude_squared = p41[0]^2 + p41[1]^2

for x, y in list_of_points_to_test:

    p = (x - x1, y - y1)

    if 0 <= p[0] * p21[0] + p[1] * p21[1] <= p21magnitude_squared:
        if 0 <= p[0] * p41[0] + p[1] * p41[1]) <= p41magnitude_squared:
            return "Inside"
        else:
            return "Outside"
    else:
        return "Outside"

就是这样。它也适用于平行四边形。

答案 5 :(得分:6)

如果无法解决矩形问题,请尝试将问题分解为更容易的问题。 将矩形划分为2个三角形,检查点是否在任何一个内部,就像它们在here

中解释的那样

基本上,你从一个点开始循环每两对线的边缘。然后使用叉积使用叉积检查点是否在两条线之间。如果已经验证了所有3个点,则该点在三角形内。这种方法的好处是它不会产生任何浮点错误,如果你检查角度就会发生错误。

答案 6 :(得分:5)

bool pointInRectangle(Point A, Point B, Point C, Point D, Point m ) {
    Point AB = vect2d(A, B);  float C1 = -1 * (AB.y*A.x + AB.x*A.y); float  D1 = (AB.y*m.x + AB.x*m.y) + C1;
    Point AD = vect2d(A, D);  float C2 = -1 * (AD.y*A.x + AD.x*A.y); float D2 = (AD.y*m.x + AD.x*m.y) + C2;
    Point BC = vect2d(B, C);  float C3 = -1 * (BC.y*B.x + BC.x*B.y); float D3 = (BC.y*m.x + BC.x*m.y) + C3;
    Point CD = vect2d(C, D);  float C4 = -1 * (CD.y*C.x + CD.x*C.y); float D4 = (CD.y*m.x + CD.x*m.y) + C4;
    return     0 >= D1 && 0 >= D4 && 0 <= D2 && 0 >= D3;}





Point vect2d(Point p1, Point p2) {
    Point temp;
    temp.x = (p2.x - p1.x);
    temp.y = -1 * (p2.y - p1.y);
    return temp;}

Points inside polygon

我刚刚使用c ++实现了AnT的答案。我使用此代码检查像素的协调(X,Y)是否位于形状内部。

答案 7 :(得分:4)

如果一个点在矩形内。在飞机上。对于数学家或大地测量学(GPS)坐标

  • 让矩形由顶点A,B,C,D设置。点为P.坐标为矩形:x,y。
  • 让我们延长矩形的边。所以我们有4个直线l AB ,l BC ,l CD ,l DA ,或者,为了简洁,l 1 ,l 2 ,l 3 ,l 4
  • 为每个l i 制作一个等式。等式:

    ˚F<子> I (P)= 0。

P是一个观点。对于属于l i 的点,等式为真。

  • 我们需要方程左侧的函数。它们是f 1 ,f 2 ,f 3 ,f 4
  • 注意,对于l i 的一侧的每个点,函数f i 大于0,对于来自另一侧的点f i < / sub>小于0.
  • 因此,如果我们检查P是否为矩形,我们只需要将p放在所有四条线的正确边上。因此,我们必须检查四个功能的标志。
  • 但这条线的哪一侧是正确的,矩形属于哪一侧?它是侧面,其中位于不属于该线的矩形顶点。为了检查,我们可以选择两个不属于顶点的任何一个。
  • 所以,我们必须检查一下:

    f AB (P)f AB (C)&gt; = 0

    f BC (P)f BC (D)&gt; = 0

    f CD (P)f CD (A)&gt; = 0

    f DA (P)f DA (B)&gt; = 0

不等式不严格,因为如果一个点在边界上,它也属于矩形。如果边界上不需要点,则可以更改严格的点的不等式。但是当你在浮点运算中工作时,选择是无关紧要的。

  • 对于一个点,即矩形,所有四个不等式都为真。请注意,它也适用于每个凸多边形,只有线/方程的数量会有所不同。
  • 唯一剩下的就是得到一条线经过两点的等式。这是一个众所周知的线性方程。让我们把它写成AB行和P点:

    f AB (P)≡ (x A -x B )(y P -y B ) - (y A < / sub> -y B )(x P -x B

检查可以简化 - 让我们沿着矩形顺时针 - A,B,C,D,A。然后所有正确的边都在线的右边。因此,我们无需与另一个顶点所在的一侧进行比较。我们需要检查一组较短的不等式:

f AB (P)&gt; = 0

f BC (P)&gt; = 0

f CD (P)&gt; = 0

f DA (P)&gt; = 0

但这对于普通的数学家(来自学校数学)坐标系是正确的,其中X在右边,Y在顶部。对于大地测量坐标,如GPS中使用的那样,X在顶部,Y在右边,我们必须改变不等式:

f AB (P)&lt; = 0

f BC (P)&lt; = 0

f CD (P)&lt; = 0

f DA (P)&lt; = 0

如果您不确定轴的方向,请注意这种简化的检查 - 如果您选择了正确的不等式,请检查已知位置的一个点。

答案 8 :(得分:0)

我想到的最简单的方法是将点投影到矩形的轴上。让我解释一下:

如果可以将矢量从矩形的中心到顶部或底部边缘以及左边缘或右边缘。并且您还有一个从矩形中心到点的矢量,您可以将该点投影到宽度和高度矢量上。

P =点矢量,H =高度矢量,W =宽度矢量

通过将矢量除以其幅度

来获得单位矢量W',H'

proj_P,H = P - (P.H')H' proj_P,W = P - (P.W')W'

除非我弄错了,我不认为我......(如果我错了就纠正我)但是你的点在高度矢量上的投影幅度小于高度矢量的幅度(这是矩形高度的一半)和点在宽度向量上的投影幅度,那么你的矩形内部就有一个点。

如果您有通用坐标系,则可能必须使用向量减法计算高度/宽度/点向量。矢量投影是惊人的!记得那个。

答案 9 :(得分:0)

接连无光的答案。我们需要使用 https://math.stackexchange.com/questions/190111/how-to-check-if-a-point-is-inside-a-rectangle/190373#190373 使它起作用的解决方案

以下不起作用
0 <=点(AB,AM)<=点(AB,AB)&& 0 <=点(BC,BM)<=点(BC,BC)

以下作品
0 <=点(AB,AM)<=点(AB,AB)&& 0 <=点(AM,AC)<=点(AC,AC)

您通过在JavaScript控制台中粘贴以下内容进行检查 //相同的Javascript解决方案

            var screenWidth = 320;
            var screenHeight = 568;
            var appHeaderWidth = 320;
            var AppHeaderHeight = 65;
            var regionWidth = 200;
            var regionHeight = 200;

            this.topLeftBoundary = {
                A: {x: 0, y: AppHeaderHeight},
                B: {x: regionWidth, y: AppHeaderHeight},
                C: {x: 0, y: regionHeight + AppHeaderHeight},
                D: {x: regionWidth, y: regionHeight + AppHeaderHeight}
            }

            this.topRightBoundary = {
                A: {x: screenWidth, y: AppHeaderHeight},
                B: {x: screenWidth - regionWidth, y: AppHeaderHeight},
                C: {x: screenWidth, y: regionHeight + AppHeaderHeight},
                D: {x: screenWidth - regionWidth, y: regionHeight + AppHeaderHeight}
            }

            this.bottomRightBoundary = {
                A: {x: screenWidth, y: screenHeight},
                B: {x: screenWidth - regionWidth, y: screenHeight},
                C: {x: screenWidth, y: screenHeight - regionHeight},
                D: {x: screenWidth - regionWidth, y: screenHeight - regionHeight}
            }

            this.bottomLeftBoundary = {
                A: {x: 0, y: screenHeight},
                B: {x: regionWidth, y: screenHeight},
                C: {x: 0, y: screenHeight - regionHeight},
                D: {x: regionWidth, y: screenHeight - regionHeight}
            }
            console.log(this.topLeftBoundary);
            console.log(this.topRightBoundary);
            console.log(this.bottomRightBoundary);
            console.log(this.bottomLeftBoundary);

            checkIfTapFallsInBoundary = function (region, point) {
                console.log("region " + JSON.stringify(region));
                console.log("point" + JSON.stringify(point));

                var r = region;
                var m = point;

                function vector(p1, p2) {
                    return {
                        x: (p2.x - p1.x),
                        y: (p2.y - p1.y)
                    };
                }

                function dot(u, v) {
                    console.log("DOT " + (u.x * v.x + u.y * v.y));
                    return u.x * v.x + u.y * v.y;
                }

                function pointInRectangle(m, r) {
                    var AB = vector(r.A, r.B);
                    var AM = vector(r.A, m);
                    var AC = vector(r.A, r.C);
                    var BC = vector(r.B, r.C);
                    var BM = vector(r.B, m);

                    console.log("AB " + JSON.stringify(AB));
                    console.log("AM " + JSON.stringify(AM));
                    console.log("AM " + JSON.stringify(AC));
                    console.log("BC " + JSON.stringify(BC));
                    console.log("BM " + JSON.stringify(BM));

                    var dotABAM = dot(AB, AM);
                    var dotABAB = dot(AB, AB);
                    var dotBCBM = dot(BC, BM);
                    var dotBCBC = dot(BC, BC);
                    var dotAMAC = dot(AM, AC);
                    var dotACAC = dot(AC, AC);

                    console.log("ABAM " + JSON.stringify(dotABAM));
                    console.log("ABAB " + JSON.stringify(dotABAB));
                    console.log("BCBM " + JSON.stringify(dotBCBM));
                    console.log("BCBC " + JSON.stringify(dotBCBC));
                    console.log("AMAC " + JSON.stringify(dotAMAC));
                    console.log("ACAC" + JSON.stringify(dotACAC));

                    var check = ((0 <= dotABAM && dotABAM <= dotABAB) && (0 <= dotBCBM && dotBCBM <= dotBCBC));
                    console.log(" first check" + check);
                    var check = ((0 <= dotABAM && dotABAM <= dotABAB) && (0 <= dotAMAC && dotAMAC <= dotACAC));
                    console.log("second check" + check);
                    return check;
                }

                return pointInRectangle(m, r);
            }

        //var point = {x: 136, y: 342};

            checkIfTapFallsInBoundary(topLeftBoundary, {x: 136, y: 342});
            checkIfTapFallsInBoundary(topRightBoundary, {x: 136, y: 274});
            checkIfTapFallsInBoundary(bottomRightBoundary, {x: 141, y: 475});
            checkIfTapFallsInBoundary(bottomRightBoundary, {x: 131, y: 272});
            checkIfTapFallsInBoundary(bottomLeftBoundary, {x: 131, y: 272});