代码之家  ›  专栏  ›  技术社区  ›  avd

确定点是否位于矩形内

  •  70
  • avd  · 技术社区  · 16 年前

    我想知道一个点是否位于矩形内。矩形可以以任何方式定向,无需对齐轴。

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

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

    9 回复  |  直到 7 年前
        1
  •  74
  •   AnT stands with Russia    7 年前

    矩形是如何表示的?三分?四分?点,边和角度?两点一边?还有别的吗?在不知道的情况下,任何试图回答你问题的尝试都只会有纯粹的学术价值。

    在任何情况下 凸面的 多边形(包括矩形)测试非常简单:检查多边形的每条边,假设每条边都是逆时针方向的,并测试点是否位于 向左 边缘(在左手边的半平面上)。如果所有的边都通过了测试-那点就在里面。如果至少有一个失败了-关键就在外面。

    为了检验这一点 (xp, yp) 位于边缘的左侧 (x1, y1) - (x2, y2) ,你只需要计算一下

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

    如果 D > 0 ,该点位于左侧。如果 D < 0 ,点在右侧。如果 D = 0 ,点在直线上。


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

    …为了检验这一点 (经验值,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
    

    如果 大于等于0 ,该点位于左侧。如果 <0 ,点在右侧。如果 d=0 ,点在直线上。

    但是,这个测试同样适用于任何凸多边形,这意味着它对于矩形来说可能太通用了。一个矩形可能允许更简单的测试…例如,在矩形(或任何其他平行四边形)中 A 和 B 对于相反(即平行)边具有相同的量值但不同的符号,可以利用这些符号简化测试。

        2
  •  38
  •   Eric Bainville    13 年前

    假设矩形由三个点a、b、c表示,ab和bc垂直,则只需检查查询点m在ab和bc上的投影:

    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),dot(ab,ab)=29,dot(bc,bc)=10。

    对于查询点m(4,2),我们有am=(-1,2),bm=(4,0),dot(ab,am)=9,dot(bc,bm)=4。m在矩形内。

    对于查询点p(6,1),我们有ap=(1,1),bp=(6,-1),dot(ab,ap)=-3,dot(bc,bp)=3。p不在矩形内,因为它在ab侧的投影不在ab段内。

        3
  •  15
  •   Jonas Elfström    12 年前
    # 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
  •  15
  •   matt burns    10 年前

    我借用了埃里克·班维尔的回答:

    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.
    

    下面是一个代码笔,用于将输出绘制为可视测试:) http://codepen.io/mattburns/pen/jrrprN

    enter image description here

        5
  •  11
  •   Community Mohan Dere    9 年前

    我意识到这是一个古老的线索,但是对于任何有兴趣从纯数学的角度来看待这个问题的人来说,在数学堆栈交换上有一个极好的线索,这里是:

    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"
    

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

        6
  •  6
  •   Cristian T    16 年前

    如果你不能解决矩形的问题,试着把问题分成容易的问题。 将矩形分成两个三角形,检查点是否在其中任何一个三角形内,如中所述 here

    从本质上讲,你从一个点开始,每两对直线上的边循环一次。然后使用叉积检查点是否在使用叉积的两行之间。如果对所有3个点都进行了验证,则该点位于三角形内部。这个方法的好处是它不会产生任何浮点错误,如果你检查角度的话会发生这种错误。

        7
  •  5
  •   Alghyaline    9 年前
    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++实现了蚂蚁的答案。我用这段代码检查像素的坐标(x,y)是否位于形状内部。

        8
  •  4
  •   Gangnus    8 年前

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

    • 让矩形由顶点A、B、C、D设置。点为P。坐标为矩形:X、Y。
    • 让我们延长矩形的边。所以我们有4条直线 抗体 ,L 公元前 ,L 光盘 ,L DA 或者,简短地说,我 一 ,L 二 ,L 三 ,L 四 .
    • 为每一个 我 . 方程式类型:

      f 我 (p)=0。

    P是一个点。对于点,属于L 我 ,方程是真的。

    • 我们需要方程左边的函数。他们是F 一 ,f 二 ,f 三 ,f 四 .
    • 注意,从l的一边看每一点 我 函数f 我 大于0,对于来自另一侧的点f 我 小于0。
    • 所以,如果我们检查p是否在矩形中,我们只需要p在所有四条直线的正确边上。所以,我们必须检查四个功能的标志。
    • 但是直线的哪一边是正确的,矩形属于哪一边?它是边,矩形的顶点不属于直线。为了检查,我们可以选择两个不属于顶点中的任何一个。
    • 所以,我们必须检查一下:

      f 抗体 (p)f 抗体 (c)>=0

      f 公元前 (p)f 公元前 (d)>=0

      f 光盘 (p)f 光盘 (a)>=0

      f 达 (p)f DA (b)>=0

    不满足条件并不严格,因为如果一个点在边界上,它也属于矩形。如果不需要边界上的点,可以将不等式改为严格不等式。但是当你在浮点运算中工作时,这个选择是不相关的。

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

      f 抗体 (p) (X) 一 -X 乙 (y) P -Y 乙 -(Y) 一 -Y 乙 (X) P -X 乙 )

    支票可以简化-让我们沿着矩形走 顺时针方向的 -A,B,C,D,A。那么所有正确的边都在直线的右边。所以,我们不必和另一个眩晕的那一边相比。我们需要检查一组较短的不等式:

    f 抗体 (p)>=0

    f 公元前 (p)>=0

    f 光盘 (p)>=0

    f 达 (p)>=0

    但这对正常人来说是正确的, 数学家 (来自学校数学)一组坐标,其中x在右边,y在上面。为了 大地测量学 坐标,如GPS中使用的,X在顶部,Y在右侧,我们必须改变不等式:

    f 抗体 (P)<=0

    f 公元前 (P)<=0

    f 光盘 (P)<=0

    f 达 (P)<=0

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

        9
  •  0
  •   Matthew    15 年前

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

    如果可以得到从矩形中心到上边缘或下边缘以及左边缘或右边缘的矢量。你也有一个从矩形中心到你的点的向量,你可以把这个点投影到你的宽度和高度向量上。

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

    将向量除以其大小,得到单位向量w',h'

    项目,H=P-(P.H')H' 项目,W=P-(P.W')W'

    除非我错了,我不认为我是…(如果我错了,请纠正我)但是如果你的点在高度向量上的投影大小小于高度向量的大小(它是矩形高度的一半)和你的点在宽度向量上的投影大小,那么你的点在你的长方形。

    如果有通用坐标系,则可能需要使用矢量减法计算高度/宽度/点向量。矢量投影是惊人的!记住这一点。