점이 사각형 안에 있는지 확인하고 싶습니다. 직사각형은 어떤 방식 으로든 방향이 지정 될 수 있으며 축 정렬이 필요하지 않습니다.
제가 생각할 수있는 한 가지 방법은 직사각형과 점 좌표를 회전하여 직사각형 축을 정렬 한 다음 단순히 점의 좌표가 직사각형의 좌표 내에 있는지 여부를 테스트하는 것입니다.
위의 방법에는 회전이 필요하므로 부동 소수점 연산이 필요합니다. 이를 수행하는 다른 효율적인 방법이 있습니까?
점이 사각형 안에 있는지 확인하고 싶습니다. 직사각형은 어떤 방식 으로든 방향이 지정 될 수 있으며 축 정렬이 필요하지 않습니다.
제가 생각할 수있는 한 가지 방법은 직사각형과 점 좌표를 회전하여 직사각형 축을 정렬 한 다음 단순히 점의 좌표가 직사각형의 좌표 내에 있는지 여부를 테스트하는 것입니다.
위의 방법에는 회전이 필요하므로 부동 소수점 연산이 필요합니다. 이를 수행하는 다른 효율적인 방법이 있습니까?
답변:
직사각형은 어떻게 표현됩니까? 3 점? 4 점? 포인트, 측면 및 각도? 두 점과 측면? 다른 것? 그 사실을 모르면 질문에 답하려는 시도는 순전히 학문적 가치 만 가질 것입니다.
어쨌든 모든 볼록 다각형 (직사각형 포함)에 대해 테스트는 매우 간단합니다. 각 가장자리가 시계 반대 방향으로 향한다고 가정하고 다각형의 각 가장자리를 확인하고 점이 가장자리의 왼쪽 ( 왼쪽 에 있는지 테스트) -손 절반 평면). 모든 모서리가 테스트를 통과하면 점이 안쪽에 있습니다. 하나라도 실패하면 지점이 외부에 있습니다.
점이 (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
.
직사각형이 AB와 BC가 수직 인 세 점 A, B, C로 표시된다고 가정하면 AB 및 BC에서 쿼리 점 M의 투영 만 확인하면됩니다.
0 <= dot(AB,AM) <= dot(AB,AB) &&
0 <= dot(BC,BM) <= dot(BC,BC)
AB
좌표 (Bx-Ax, By-Ay)가있는 벡터 AB 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입니다. 측면 AB의 투영이 세그먼트 AB 내부가 아니기 때문에 P는 직사각형 내부가 아닙니다.
나는 Eric Bainville의 대답을 빌 렸습니다.
0 <= dot(AB,AM) <= dot(AB,AB) && 0 <= dot(BC,BM) <= dot(BC,BC)
자바 스크립트에서 다음과 같이 보입니다.
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
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
. 세 점이 이론상 임의의 직사각형을 나타내는 유효한 방법이지만 실제로 는 일반적인 경우에 interger 좌표에서 정확하게 수행하는 것은 불가능합니다 . 일반적으로 직사각형에 매우 가깝지만 여전히 직사각형이 아닌 평행 사변형으로 끝납니다.
나는 이것이 오래된 스레드라는 것을 알고 있지만 순전히 수학적 관점에서 이것을 보는 데 관심이있는 사람에게는 수학 스택 교환에 대한 훌륭한 스레드가 있습니다.
/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의 답변을 구현했습니다. 이 코드를 사용하여 픽셀의 좌표 (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 <= dot (AB, AM) <= dot (AB, AB) && 0 <= dot (BC, BM) <= dot (BC, BC)
아래 작품
0 <= dot (AB, AM) <= dot (AB, AB) && 0 <= dot (AM, AC) <= dot (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});