我想查找一个点是否位于矩形内。矩形可以以任何方式定向,并且不需要轴对齐。
我能想到的一种方法是旋转矩形和点的坐标以使矩形轴对齐,然后简单地测试点的坐标是否位于矩形的坐标内。
上述方法需要旋转,因此需要浮点运算。还有其他有效的方法吗?
我想查找一个点是否位于矩形内。矩形可以以任何方式定向,并且不需要轴对齐。
我能想到的一种方法是旋转矩形和点的坐标以使矩形轴对齐,然后简单地测试点的坐标是否位于矩形的坐标内。
上述方法需要旋转,因此需要浮点运算。还有其他有效的方法吗?
Answers:
矩形如何显示?三分?四分?点,边和角度?一分两分?还有吗 不知不觉中,任何回答您的问题的尝试都将仅具有学术价值。
在任何情况下,对于任何凸多边形(包括矩形)的测试是非常简单的:检查多边形的每个边缘,假设每个边缘被定向在反时针方向,以及测试是否点位于向左的边缘的(在左-手半平面)。如果所有边缘均通过测试,则该点在内部。如果至少有一个失败-该点在外面。
为了测试该点是否(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
,则该点在线上。
但是,该测试同样适用于任何凸多边形,这意味着它对于矩形可能太通用了。矩形可能允许更简单的测试...例如,在矩形(或任何其他平行四边形)中,的值A
和值B
具有相同的大小,但相对(即平行)边缘的符号不同,可以利用该值简化测试。
A'
和B'
可以由A'=B
和给出B'=-A
。3.A xp + B yp
两条边都没有计算点,因此将它们合并为一个测试。然后,您在矩形中的测试变为(v_min < A xp + B yp < v_max) && ( w_min < B xp - A yp < w_max )
。
v_min
,等等?
v_min
是A x + B y
矩形内部所有值的最小值(在角处评估时的最小值)。v_max
是相应的最大值。该w_?
值是相同的,但对Bx - A y
。
假设矩形由三个点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),点(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不在矩形内部,因为P在侧面AB上的投影不在线段AB内。
我从埃里克·班维尔的答案中借来的:
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
mouseover
在项目中使用了一个事件,因此,每当鼠标悬停在应该位于矩形内的点上时,它将在鼠标周围显示一个黑圈,而在矩形外则不显示任何内容。我需要帮助才能使其正常工作,但我很困惑。
mouseover
应该是mousemove
,只是拼写错误。
# 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
a
,b
和d
。虽然从理论上讲,三点是表示任意矩形的有效方法,但实际上在一般情况下,不可能精确地进行整数坐标处理。通常情况下,将以平行四边形结束,该平行四边形非常接近矩形,但仍然不是矩形。
我意识到这是一个旧线程,但是对于任何有兴趣从纯粹的数学角度来看这件事的人,数学堆栈交换中都有一个很棒的线程,这里:
/math/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之间。这等效于以p1为原点,沿着矩形的长度和宽度投影向量p-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"
就是这样。它也适用于平行四边形。
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;}
我刚刚使用c ++实现了AnT的Answer。我使用此代码检查像素的坐标(X,Y)是否位于形状内。
对每一个l i做一个方程。方程式如下:
f i(P)= 0。
P是一点。对于属于l i的点,等式成立。
因此,我们必须检查以下内容:
f AB(P)f AB(C)> = 0
f BC(P)f BC(D)> = 0
f CD(P)f CD(A)> = 0
f DA(P)f DA(B)> = 0
不等式并不严格,因为如果点在边界上,则它也属于矩形。如果不需要边界上的点,则可以将不等式更改为严格的等式。但是,当您进行浮点运算时,选择是无关紧要的。
剩下的唯一事情就是得到一条穿过两点的直线的方程式。这是一个众所周知的线性方程。让我们为AB行和P点编写它:
f AB(P)≡(x A -x B)(y P -y B)-(y A -y B)(x P -x B)
检查可以简化-让我们顺时针旋转矩形-A,B,C,D,A。然后所有正确的边都将在线的右边。因此,我们不必与另一个顶点所在的那一侧进行比较。我们需要检查一组较短的不等式:
f AB(P)> = 0
f BC(P)> = 0
f CD(P)> = 0
f DA(P)> = 0
但这对于正常的数学家(来自学校数学)坐标集是正确的,其中X在右边,Y在顶部。对于GPS中使用的大地测量坐标,其中X在顶部,Y在右侧,我们必须将这些等式转向:
f AB(P)<= 0
f BC(P)<= 0
f CD(P)<= 0
f DA(P)<= 0
如果不确定轴的方向,请小心进行此简化检查-如果选择了正确的不等式,请检查已知位置的一个点。
我想到的最简单的方法是将点投影到矩形的轴上。让我解释:
如果可以从矩形的中心到顶部或底部边缘以及左侧或右侧边缘获取矢量。而且您还具有一个从矩形中心到点的向量,您可以将该点投影到宽度和高度向量上。
P =点向量,H =高度向量,W =宽度向量
通过将向量除以其大小来获得单位向量W',H'
proj_P,H = P-(P.H')H'proj_P,W = P-(P.W')W'
除非我没记错,否则我认为我不是...(如果我错了,请纠正我),但是如果您的点在高度矢量上的投影大小小于高度矢量的大小(即矩形高度的一半)和点在宽度矢量上的投影大小是,那么您在矩形内部就有一个点。
如果您有通用坐标系,则可能必须使用矢量减法计算出高度/宽度/点矢量。矢量投影太神奇了!记住这一点。
继续亚光回答。我们需要使用 /math/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});