점이 삼각형 안에 있는지 쉽게 확인할 수 있습니까? 3D가 아니라 2D입니다.
점이 삼각형 안에 있는지 쉽게 확인할 수 있습니까? 3D가 아니라 2D입니다.
답변:
일반적으로 가장 단순하고 (최상의 최적의) 알고리즘은 포인트에 의해 생성 된 반 평면의 측면을 확인하는 것입니다.
다음은 성능 문제를 포함하여 GameDev에 대한 이 주제의 고품질 정보입니다 .
시작하는 데 필요한 코드는 다음과 같습니다.
float sign (fPoint p1, fPoint p2, fPoint p3)
{
return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
}
bool PointInTriangle (fPoint pt, fPoint v1, fPoint v2, fPoint v3)
{
float d1, d2, d3;
bool has_neg, has_pos;
d1 = sign(pt, v1, v2);
d2 = sign(pt, v2, v3);
d3 = sign(pt, v3, v1);
has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
return !(has_neg && has_pos);
}
다음 방정식 시스템을 풉니 다.
p = p0 + (p1 - p0) * s + (p2 - p0) * t
점 p
은 0 <= s <= 1
and 0 <= t <= 1
및 안에 삼각형 안에 s + t <= 1
있습니다.
s
, t
및 1 - s - t
호출되는 무게 중심 좌표 점의를 p
.
s + t <= 1
의미 합니다 . s <= 1
t <= 1
s >= 0
t >= 0
Andreas Brinck에 동의 하는데 ,이 작업에는 무게 중심 좌표가 매우 편리합니다. 매번 방정식 시스템을 풀 필요가 없습니다. 분석 솔루션 만 평가하면됩니다. Andreas 의 표기법을 사용 하면 해결책은 다음과 같습니다.
s = 1/(2*Area)*(p0y*p2x - p0x*p2y + (p2y - p0y)*px + (p0x - p2x)*py);
t = 1/(2*Area)*(p0x*p1y - p0y*p1x + (p0y - p1y)*px + (p1x - p0x)*py);
Area
삼각형의 (부호있는) 영역은 어디 입니까?
Area = 0.5 *(-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y);
그냥 평가 s
, t
및 1-s-t
. 요점p
모두 양수인 경우에만 삼각형 안에 있습니다.
편집 : 영역에 대한 위의 표현은 삼각형 노드 번호가 시계 반대 방향이라고 가정합니다. 번호가 시계 방향 인 경우이 표현식은 음수 영역을 반환하지만 정확한 크기를 갖습니다. 테스트 자체 ( s>0 && t>0 && 1-s-t>0
)는 숫자의 방향에 의존하지 않지만, 위의 표현식은1/(2*Area)
에 삼각형 노드 방향이 변경되는 경우 변경 부호도 입니다.
편집 2 : 계산 효율을 높이 려면 아래의 coproc 주석을 참조하십시오 (이는 삼각형 노드의 방향 (시계 방향 또는 시계 반대 방향)을 사전에 알고 있다면 2*Area
식에서 s
및 t
피하십시오). Andreas Brinck 의 답변 아래 주석에서 Perro Azul 의 jsfiddle-code를 참조하십시오 .
2*Area
, 즉 계산 s´=2*|Area|*s
및 t´=2*|Area|*t
(시계 방향 또는 시계 반대 방향으로 포인트의 방향을 알 Area
수없는 경우 물론 부호를 확인해야하지만 그렇지 않은 경우에도 효율을 향상시킬 수 있음) 확인해야하기 때문에 check s>0
가 충분 하므로 계산해야합니다 s´>0
. 그리고 확인 1-s-t>0
하는 대신 확인 하면 충분합니다 s´+t´<2*|Area|
.
p0->p1->p2
인 반 시계 방향 에 직교 (보통 시계 방향 으로 스크린 좌표 )는 Area
이 방법에 의해 산출 된 긍정적 것이다.
Google에 마지막으로 시도하고이 페이지를 찾기 전에이 코드를 작성 했으므로 공유하겠다고 생각했습니다. 기본적으로 Kisielewicz 답변의 최적화 된 버전입니다. 나는 Barycentric 방법도 살펴 보았지만 Wikipedia 기사에서 판단하는 것이 더 효율적인 방법을 보는 데 어려움을 겪고 있습니다 (더 깊은 동등성이 있다고 생각합니다). 어쨌든이 알고리즘은 나누기를 사용하지 않는 이점이 있습니다. 잠재적 인 문제는 방향에 따른 가장자리 감지 동작입니다.
bool intpoint_inside_trigon(intPoint s, intPoint a, intPoint b, intPoint c)
{
int as_x = s.x-a.x;
int as_y = s.y-a.y;
bool s_ab = (b.x-a.x)*as_y-(b.y-a.y)*as_x > 0;
if((c.x-a.x)*as_y-(c.y-a.y)*as_x > 0 == s_ab) return false;
if((c.x-b.x)*(s.y-b.y)-(c.y-b.y)*(s.x-b.x) > 0 != s_ab) return false;
return true;
}
요컨대, 아이디어는 이것입니다. 점 AB와 AC 둘 다의 왼쪽 또는 오른쪽에 있습니까? 사실이면 안에있을 수 없습니다. 거짓 인 경우, 조건을 만족시키는 것은 "콘"안에 있습니다. 이제 trigon (삼각형) 내부의 점이 BC (및 CA)와 AB의 같은면에 있어야한다는 것을 알았으므로 차이점이 있는지 확인합니다. 그렇다면 내부에있을 수 없으며, 그렇지 않으면 내부에 있어야합니다.
계산에 사용 된 일부 키워드는 반쪽 평면과 결정 요인 (2x2 교차 곱)입니다. 아마도 더 교육적인 방법은 아마도 각 선 AB, BC 및 CA와 같은 쪽 (왼쪽 또는 오른쪽)에있는 경우 내부 점으로 생각하는 것입니다. 그러나 위의 방법은 일부 최적화에 더 적합한 것으로 보입니다.
andreasdr 및 Perro Azul이 게시 한 barycentric 방법의 C # 버전입니다. 면적 계산하면 피할 수 있습니다 s
및 t
반대 표시를해야합니다. 꽤 철저한 단위 테스트로 올바른 동작을 확인했습니다.
public static bool PointInTriangle(Point p, Point p0, Point p1, Point p2)
{
var s = p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * p.X + (p0.X - p2.X) * p.Y;
var t = p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * p.X + (p1.X - p0.X) * p.Y;
if ((s < 0) != (t < 0))
return false;
var A = -p1.Y * p2.X + p0.Y * (p2.X - p1.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y;
return A < 0 ?
(s <= 0 && s + t >= A) :
(s >= 0 && s + t <= A);
}
[ edit ]
@Pierre의 제안 된 수정을 수락했습니다. 의견보기
barycentric 메소드의 Java 버전 :
class Triangle {
Triangle(double x1, double y1, double x2, double y2, double x3,
double y3) {
this.x3 = x3;
this.y3 = y3;
y23 = y2 - y3;
x32 = x3 - x2;
y31 = y3 - y1;
x13 = x1 - x3;
det = y23 * x13 - x32 * y31;
minD = Math.min(det, 0);
maxD = Math.max(det, 0);
}
boolean contains(double x, double y) {
double dx = x - x3;
double dy = y - y3;
double a = y23 * dx + x32 * dy;
if (a < minD || a > maxD)
return false;
double b = y31 * dx + x13 * dy;
if (b < minD || b > maxD)
return false;
double c = det - a - b;
if (c < minD || c > maxD)
return false;
return true;
}
private final double x3, y3;
private final double y23, x32, y31, x13;
private final double det, minD, maxD;
}
위의 코드는 오버플로가 없다고 가정하면 정수로 정확하게 작동합니다. 또한 시계 방향 및 반 시계 방향 삼각형에서도 작동합니다. 동일 선형 삼각형에서는 작동하지 않지만 det == 0을 테스트하여 확인할 수 있습니다.
같은 삼각형으로 다른 점을 테스트하려는 경우 barycentric 버전이 가장 빠릅니다.
Barycentric 버전은 3 개의 삼각형 포인트에서 대칭이 아니므로 부동 소수점 반올림 오류로 인해 Kornel Kisielewicz의 Edge Half-plane 버전보다 일관성이 떨어질 수 있습니다.
크레딧 : 나는 Wikipedia의 기사에서 barycentric 좌표에 위의 코드를 만들었습니다.
간단한 방법은 다음과 같습니다.
점을 삼각형의 세 꼭짓점 각각에 연결하는 벡터를 찾고 해당 벡터 사이의 각도를 합산합니다. 각도의 합이 2 * pi 인 경우 점은 삼각형 안에 있습니다.
대안을 설명하는 두 가지 좋은 사이트는 다음과 같습니다.
Barrycentric 좌표에 분석 솔루션을 사용하여 ( Andreas Brinck가 지적함 )
"비용이 많이 드는"작업의 수를 최소화 할 수 있습니다.
function ptInTriangle(p, p0, p1, p2) {
var dX = p.x-p2.x;
var dY = p.y-p2.y;
var dX21 = p2.x-p1.x;
var dY12 = p1.y-p2.y;
var D = dY12*(p0.x-p2.x) + dX21*(p0.y-p2.y);
var s = dY12*dX + dX21*dY;
var t = (p2.y-p0.y)*dX + (p0.x-p2.x)*dY;
if (D<0) return s<=0 && t<=0 && s+t>=D;
return s>=0 && t>=0 && s+t<=D;
}
Perro Azul jsfiddle 에 코드를 붙여 넣거나 아래의 "코드 조각 실행"을 클릭하여 사용해보십시오.
var ctx = $("canvas")[0].getContext("2d");
var W = 500;
var H = 500;
var point = { x: W / 2, y: H / 2 };
var triangle = randomTriangle();
$("canvas").click(function(evt) {
point.x = evt.pageX - $(this).offset().left;
point.y = evt.pageY - $(this).offset().top;
test();
});
$("canvas").dblclick(function(evt) {
triangle = randomTriangle();
test();
});
test();
function test() {
var result = ptInTriangle(point, triangle.a, triangle.b, triangle.c);
var info = "point = (" + point.x + "," + point.y + ")\n";
info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";
info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";
info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";
info += "result = " + (result ? "true" : "false");
$("#result").text(info);
render();
}
function ptInTriangle(p, p0, p1, p2) {
var A = 1/2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
var sign = A < 0 ? -1 : 1;
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign;
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign;
return s > 0 && t > 0 && (s + t) < 2 * A * sign;
}
function render() {
ctx.fillStyle = "#CCC";
ctx.fillRect(0, 0, 500, 500);
drawTriangle(triangle.a, triangle.b, triangle.c);
drawPoint(point);
}
function drawTriangle(p0, p1, p2) {
ctx.fillStyle = "#999";
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px monospace";
ctx.fillText("1", p0.x, p0.y);
ctx.fillText("2", p1.x, p1.y);
ctx.fillText("3", p2.x, p2.y);
}
function drawPoint(p) {
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomTriangle() {
return {
a: { x: rand(0, W), y: rand(0, H) },
b: { x: rand(0, W), y: rand(0, H) },
c: { x: rand(0, W), y: rand(0, H) }
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<pre>Click: place the point.
Double click: random triangle.</pre>
<pre id="result"></pre>
<canvas width="500" height="500"></canvas>
선도하는 :
이것은 Kornel Kisielewicz 솔루션 (25 회상, 1 회 저장, 15 차감, 6 곱셈, 5 회 비교) 과 상당히 잘 비교되며 시계 방향 / 시계 반대 방향 감지가 필요한 경우에 더 좋습니다 (6 회 호출, 1 더하기, 2 빼기 필요) , rhgb에 의해 지적 된 바와 같이 , 분석 용액 결정기를 사용하여 그 자체로 2 개의 곱셈 및 1 개의 비교 .
효율적인 파이썬 구현 은 다음과 같습니다 .
def PointInsideTriangle2(pt,tri):
'''checks if point pt(2) is inside triangle tri(3x2). @Developer'''
a = 1/(-tri[1,1]*tri[2,0]+tri[0,1]*(-tri[1,0]+tri[2,0])+ \
tri[0,0]*(tri[1,1]-tri[2,1])+tri[1,0]*tri[2,1])
s = a*(tri[2,0]*tri[0,1]-tri[0,0]*tri[2,1]+(tri[2,1]-tri[0,1])*pt[0]+ \
(tri[0,0]-tri[2,0])*pt[1])
if s<0: return False
else: t = a*(tri[0,0]*tri[1,1]-tri[1,0]*tri[0,1]+(tri[0,1]-tri[1,1])*pt[0]+ \
(tri[1,0]-tri[0,0])*pt[1])
return ((t>0) and (1-s-t>0))
예제 출력 :
속도를 찾고 있다면 여기 도움이 될 수있는 절차가 있습니다.
좌표에서 삼각형 정점을 정렬합니다. 이것은 최악의 세 가지 비교가 필요합니다. Y0, Y1, Y2를 세 가지 정렬 값으로 설정하십시오. 3 개의 수평을 그려 평면을 2 개의 반 평면과 2 개의 슬래브로 분할합니다. Y를 쿼리 지점의 좌표로 지정하십시오.
if Y < Y1
if Y <= Y0 -> the point lies in the upper half plane, outside the triangle; you are done
else Y > Y0 -> the point lies in the upper slab
else
if Y >= Y2 -> the point lies in the lower half plane, outside the triangle; you are done
else Y < Y2 -> the point lies in the lower slab
두 번의 비교가 더 필요합니다. 보시다시피 "경계 슬래브"외부의 포인트에 대해서는 빠른 거부가 이루어집니다.
선택적으로 가로 좌표에 테스트를 제공하여 왼쪽과 오른쪽에서 빠르게 거부 할 수 있습니다 (X <= X0' or X >= X2'
). 이렇게하면 빠른 경계 상자 테스트가 동시에 구현되지만 가로 좌표도 정렬해야합니다.
결국 관련 슬래브 (상단 또는 하단)를 구분하는 삼각형의 양면에 대해 주어진 점의 부호를 계산해야합니다. 시험 형식은 다음과 같습니다.
((X - Xi) * (Y - Yj) > (X - Xi) * (Y - Yj)) == ((X - Xi) * (Y - Yk) > (X - Xi) * (Y - Yk))
의 전체 토론 i, j, k
조합에 (정렬 결과에 따라 6 가지가 있음)는이 답변의 범위를 벗어 났으며 "독자에게 연습으로 남겨 두었습니다"; 효율성을 위해서는 하드 코딩해야합니다.
이 솔루션이 복잡하다고 생각되는 경우, 경계 상자 테스트에 실패한 경우 간단한 비교 (일부 사전 계산 가능)와 6 개의 뺄셈 및 4 개의 곱이 주로 포함됩니다. 후자의 비용은 최악의 경우와 같이 테스트 포인트를 양측과 비교하는 것을 피할 수 없으므로 극복하기가 어렵습니다 (다른 답변의 방법은 비용이 낮지 않으며 일부는 15 뺄셈과 6 곱하기, 때로는 나눗셈과 같이 악화시킵니다).
업데이트 : 전단 변형으로 더 빠름
바로 위에서 설명한 것처럼 두 개의 비교를 사용하여 세 개의 정점 좌표로 구분 된 네 개의 수평 밴드 중 하나의 내부에서 점을 빠르게 찾을 수 있습니다.
선택적으로 하나 또는 두 개의 추가 X 테스트를 수행하여 경계 상자의 내부를 확인할 수 있습니다 (점선).
그런 다음로 주어진 "전단"변환을 고려하십시오 X'= X - m Y, Y' = Y
. 여기서 가장 높은 모서리 m
의 기울기 DX/DY
가 있습니다. 이 변환은 삼각형의이면을 수직으로 만듭니다. 그리고 당신은 당신이 중간 수평의 어느쪽에 있는지 알고 있기 때문에 삼각형의 한면에 대해 부호를 테스트하면 충분합니다.
기울어 진 삼각형 정점과 측면 방정식의 계수 m
뿐만 아니라 기울기를 미리 계산했다고 가정하면 최악의 경우가 필요합니다.X'
X = m Y + p
X' = X - m Y
;X >< m' Y + p'
전단 된 삼각형의 관련면에 대한 1 개의 부호 테스트 .Python의 다른 기능은 개발자의 방법 보다 빠르며 (적어도 나를 위해) Cédric Dufour 솔루션에서 영감을 얻었습니다 .
def ptInTriang(p_test, p0, p1, p2):
dX = p_test[0] - p0[0]
dY = p_test[1] - p0[1]
dX20 = p2[0] - p0[0]
dY20 = p2[1] - p0[1]
dX10 = p1[0] - p0[0]
dY10 = p1[1] - p0[1]
s_p = (dY20*dX) - (dX20*dY)
t_p = (dX10*dY) - (dY10*dX)
D = (dX10*dY20) - (dY10*dX20)
if D > 0:
return ( (s_p >= 0) and (t_p >= 0) and (s_p + t_p) <= D )
else:
return ( (s_p <= 0) and (t_p <= 0) and (s_p + t_p) >= D )
다음과 같이 테스트 할 수 있습니다.
X_size = 64
Y_size = 64
ax_x = np.arange(X_size).astype(np.float32)
ax_y = np.arange(Y_size).astype(np.float32)
coords=np.meshgrid(ax_x,ax_y)
points_unif = (coords[0].reshape(X_size*Y_size,),coords[1].reshape(X_size*Y_size,))
p_test = np.array([0 , 0])
p0 = np.array([22 , 8])
p1 = np.array([12 , 55])
p2 = np.array([7 , 19])
fig = plt.figure(dpi=300)
for i in range(0,X_size*Y_size):
p_test[0] = points_unif[0][i]
p_test[1] = points_unif[1][i]
if ptInTriang(p_test, p0, p1, p2):
plt.plot(p_test[0], p_test[1], '.g')
else:
plt.plot(p_test[0], p_test[1], '.r')
플롯하는 데 많은 시간이 걸리지 만 그리드는 개발자 코드 0.0844349861145 초에 대해 0.0195319652557 초에서 테스트됩니다 .
마지막으로 코드 주석 :
# Using barycentric coordintes, any point inside can be described as:
# X = p0.x * r + p1.x * s + p2.x * t
# Y = p0.y * r + p1.y * s + p2.y * t
# with:
# r + s + t = 1 and 0 < r,s,t < 1
# then: r = 1 - s - t
# and then:
# X = p0.x * (1 - s - t) + p1.x * s + p2.x * t
# Y = p0.y * (1 - s - t) + p1.y * s + p2.y * t
#
# X = p0.x + (p1.x-p0.x) * s + (p2.x-p0.x) * t
# Y = p0.y + (p1.y-p0.y) * s + (p2.y-p0.y) * t
#
# X - p0.x = (p1.x-p0.x) * s + (p2.x-p0.x) * t
# Y - p0.y = (p1.y-p0.y) * s + (p2.y-p0.y) * t
#
# we have to solve:
#
# [ X - p0.x ] = [(p1.x-p0.x) (p2.x-p0.x)] * [ s ]
# [ Y - p0.Y ] [(p1.y-p0.y) (p2.y-p0.y)] [ t ]
#
# ---> b = A*x ; ---> x = A^-1 * b
#
# [ s ] = A^-1 * [ X - p0.x ]
# [ t ] [ Y - p0.Y ]
#
# A^-1 = 1/D * adj(A)
#
# The adjugate of A:
#
# adj(A) = [(p2.y-p0.y) -(p2.x-p0.x)]
# [-(p1.y-p0.y) (p1.x-p0.x)]
#
# The determinant of A:
#
# D = (p1.x-p0.x)*(p2.y-p0.y) - (p1.y-p0.y)*(p2.x-p0.x)
#
# Then:
#
# s_p = { (p2.y-p0.y)*(X - p0.x) - (p2.x-p0.x)*(Y - p0.Y) }
# t_p = { (p1.x-p0.x)*(Y - p0.Y) - (p1.y-p0.y)*(X - p0.x) }
#
# s = s_p / D
# t = t_p / D
#
# Recovering r:
#
# r = 1 - (s_p + t_p)/D
#
# Since we only want to know if it is insidem not the barycentric coordinate:
#
# 0 < 1 - (s_p + t_p)/D < 1
# 0 < (s_p + t_p)/D < 1
# 0 < (s_p + t_p) < D
#
# The condition is:
# if D > 0:
# s_p > 0 and t_p > 0 and (s_p + t_p) < D
# else:
# s_p < 0 and t_p < 0 and (s_p + t_p) > D
#
# s_p = { dY20*dX - dX20*dY }
# t_p = { dX10*dY - dY10*dX }
# D = dX10*dY20 - dY10*dX20
ptInTriang([11,45],[45, 45],[45, 45] ,[44, 45])
돌려 주십시오true
JS 답변이 없으므로
Clockwise & Counter-Clockwise 솔루션 :
function triangleContains(ax, ay, bx, by, cx, cy, x, y) {
let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
return det * ((bx - ax) * (y - ay) - (by - ay) * (x - ax)) > 0 &&
det * ((cx - bx) * (y - by) - (cy - by) * (x - bx)) > 0 &&
det * ((ax - cx) * (y - cy) - (ay - cy) * (x - cx)) > 0
}
편집 : det 계산에 오타가 있었지만 ( cy - ay
대신 cx - ax
)이 수정되었습니다.
https://jsfiddle.net/jniac/rctb3gfL/
function triangleContains(ax, ay, bx, by, cx, cy, x, y) {
let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
return det * ((bx - ax) * (y - ay) - (by - ay) * (x - ax)) > 0 &&
det * ((cx - bx) * (y - by) - (cy - by) * (x - bx)) > 0 &&
det * ((ax - cx) * (y - cy) - (ay - cy) * (x - cx)) > 0
}
let width = 500, height = 500
// clockwise
let triangle1 = {
A : { x: 10, y: -10 },
C : { x: 20, y: 100 },
B : { x: -90, y: 10 },
color: '#f00',
}
// counter clockwise
let triangle2 = {
A : { x: 20, y: -60 },
B : { x: 90, y: 20 },
C : { x: 20, y: 60 },
color: '#00f',
}
let scale = 2
let mouse = { x: 0, y: 0 }
// DRAW >
let wrapper = document.querySelector('div.wrapper')
wrapper.onmousemove = ({ layerX:x, layerY:y }) => {
x -= width / 2
y -= height / 2
x /= scale
y /= scale
mouse.x = x
mouse.y = y
drawInteractive()
}
function drawArrow(ctx, A, B) {
let v = normalize(sub(B, A), 3)
let I = center(A, B)
let p
p = add(I, rotate(v, 90), v)
ctx.moveTo(p.x, p.y)
ctx.lineTo(I.x, I .y)
p = add(I, rotate(v, -90), v)
ctx.lineTo(p.x, p.y)
}
function drawTriangle(ctx, { A, B, C, color }) {
ctx.beginPath()
ctx.moveTo(A.x, A.y)
ctx.lineTo(B.x, B.y)
ctx.lineTo(C.x, C.y)
ctx.closePath()
ctx.fillStyle = color + '6'
ctx.strokeStyle = color
ctx.fill()
drawArrow(ctx, A, B)
drawArrow(ctx, B, C)
drawArrow(ctx, C, A)
ctx.stroke()
}
function contains({ A, B, C }, P) {
return triangleContains(A.x, A.y, B.x, B.y, C.x, C.y, P.x, P.y)
}
function resetCanvas(canvas) {
canvas.width = width
canvas.height = height
let ctx = canvas.getContext('2d')
ctx.resetTransform()
ctx.clearRect(0, 0, width, height)
ctx.setTransform(scale, 0, 0, scale, width/2, height/2)
}
function drawDots() {
let canvas = document.querySelector('canvas#dots')
let ctx = canvas.getContext('2d')
resetCanvas(canvas)
let count = 1000
for (let i = 0; i < count; i++) {
let x = width * (Math.random() - .5)
let y = width * (Math.random() - .5)
ctx.beginPath()
ctx.ellipse(x, y, 1, 1, 0, 0, 2 * Math.PI)
if (contains(triangle1, { x, y })) {
ctx.fillStyle = '#f00'
} else if (contains(triangle2, { x, y })) {
ctx.fillStyle = '#00f'
} else {
ctx.fillStyle = '#0003'
}
ctx.fill()
}
}
function drawInteractive() {
let canvas = document.querySelector('canvas#interactive')
let ctx = canvas.getContext('2d')
resetCanvas(canvas)
ctx.beginPath()
ctx.moveTo(0, -height/2)
ctx.lineTo(0, height/2)
ctx.moveTo(-width/2, 0)
ctx.lineTo(width/2, 0)
ctx.strokeStyle = '#0003'
ctx.stroke()
drawTriangle(ctx, triangle1)
drawTriangle(ctx, triangle2)
ctx.beginPath()
ctx.ellipse(mouse.x, mouse.y, 4, 4, 0, 0, 2 * Math.PI)
if (contains(triangle1, mouse)) {
ctx.fillStyle = triangle1.color + 'a'
ctx.fill()
} else if (contains(triangle2, mouse)) {
ctx.fillStyle = triangle2.color + 'a'
ctx.fill()
} else {
ctx.strokeStyle = 'black'
ctx.stroke()
}
}
drawDots()
drawInteractive()
// trigo
function add(...points) {
let x = 0, y = 0
for (let point of points) {
x += point.x
y += point.y
}
return { x, y }
}
function center(...points) {
let x = 0, y = 0
for (let point of points) {
x += point.x
y += point.y
}
x /= points.length
y /= points.length
return { x, y }
}
function sub(A, B) {
let x = A.x - B.x
let y = A.y - B.y
return { x, y }
}
function normalize({ x, y }, length = 10) {
let r = length / Math.sqrt(x * x + y * y)
x *= r
y *= r
return { x, y }
}
function rotate({ x, y }, angle = 90) {
let length = Math.sqrt(x * x + y * y)
angle *= Math.PI / 180
angle += Math.atan2(y, x)
x = length * Math.cos(angle)
y = length * Math.sin(angle)
return { x, y }
}
* {
margin: 0;
}
html {
font-family: monospace;
}
body {
padding: 32px;
}
span.red {
color: #f00;
}
span.blue {
color: #00f;
}
canvas {
position: absolute;
border: solid 1px #ddd;
}
<p><span class="red">red triangle</span> is clockwise</p>
<p><span class="blue">blue triangle</span> is couter clockwise</p>
<br>
<div class="wrapper">
<canvas id="dots"></canvas>
<canvas id="interactive"></canvas>
</div>
여기에 위에서 설명한 것과 동일한 방법을 사용하고 있습니다. 점이 각각 AB, BC, CA의 "동일한"면에있는 경우 ABC 안에 있습니다.
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
)을 사용하면 삼각형 감기 순서를 결정하는 것이므로이 방법은 CW 및 CCW 삼각형과 함께 작동합니다 (jsFiddle 참조).
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
대신 let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
수정 : 보고 주셔서 감사합니다
Andreas가 제공 한 barycentric 좌표 솔루션을 설명하기 위해 간단한 벡터 수학을 사용하고 싶습니다. 이해하기가 더 쉬울 것입니다.
(1-s) | v0v2 | / | v0v2 | = tp | v0v1 | / | v0v1 |
1-s = tp를 얻은 다음 1 = s + tp를 얻습니다. 이중 대시 라인에있는 1 <s + t 인 t> tp 인 경우, 벡터는 삼각형 바깥에 있고, t <= tp, 단일 대시 라인에있는 1> = s + t이면 벡터는 삼각형 안에.
그런 다음 [0, 1]에 s를 주면 삼각형 내부의 벡터에 대해 해당 t가 1> = s + t를 충족해야합니다.
결국 우리는 v = s * v02 + t * v01을 얻습니다. v는 s, t, s + t가 [0, 1]에 속하는 삼각형 안에 있습니다. 그런 다음 포인트로 번역하면
p-p0 = s * (p1-p0) + t * (p2-p0), [0, 1]에서 s, t, s + t 포함
방정식 시스템 p = p0 + s * (p1-p0) + t * (p2-p0)를 해결하는 Andreas의 솔루션과 동일하며, s, t, s + t는 [0, 1]에 속합니다.
다음은 효율적이고 문서화되었으며 세 가지 단위 테스트를 포함하는 파이썬의 솔루션입니다. 전문가 수준의 품질로 모듈 형태로 프로젝트에 바로 넣을 수 있습니다.
import unittest
###############################################################################
def point_in_triangle(point, triangle):
"""Returns True if the point is inside the triangle
and returns False if it falls outside.
- The argument *point* is a tuple with two elements
containing the X,Y coordinates respectively.
- The argument *triangle* is a tuple with three elements each
element consisting of a tuple of X,Y coordinates.
It works like this:
Walk clockwise or counterclockwise around the triangle
and project the point onto the segment we are crossing
by using the dot product.
Finally, check that the vector created is on the same side
for each of the triangle's segments.
"""
# Unpack arguments
x, y = point
ax, ay = triangle[0]
bx, by = triangle[1]
cx, cy = triangle[2]
# Segment A to B
side_1 = (x - bx) * (ay - by) - (ax - bx) * (y - by)
# Segment B to C
side_2 = (x - cx) * (by - cy) - (bx - cx) * (y - cy)
# Segment C to A
side_3 = (x - ax) * (cy - ay) - (cx - ax) * (y - ay)
# All the signs must be positive or all negative
return (side_1 < 0.0) == (side_2 < 0.0) == (side_3 < 0.0)
###############################################################################
class TestPointInTriangle(unittest.TestCase):
triangle = ((22 , 8),
(12 , 55),
(7 , 19))
def test_inside(self):
point = (15, 20)
self.assertTrue(point_in_triangle(point, self.triangle))
def test_outside(self):
point = (1, 7)
self.assertFalse(point_in_triangle(point, self.triangle))
def test_border_case(self):
"""If the point is exactly on one of the triangle's edges,
we consider it is inside."""
point = (7, 19)
self.assertTrue(point_in_triangle(point, self.triangle))
###############################################################################
if __name__ == "__main__":
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestPointInTriangle)
unittest.TextTestRunner().run(suite)
유효성을 확인하기 위해 위 알고리즘에 대한 추가 선택적 그래픽 테스트가 있습니다.
import random
from matplotlib import pyplot
from triangle_test import point_in_triangle
###############################################################################
# The area #
size_x = 64
size_y = 64
# The triangle #
triangle = ((22 , 8),
(12 , 55),
(7 , 19))
# Number of random points #
count_points = 10000
# Prepare the figure #
figure = pyplot.figure()
axes = figure.add_subplot(111, aspect='equal')
axes.set_title("Test the 'point_in_triangle' function")
axes.set_xlim(0, size_x)
axes.set_ylim(0, size_y)
# Plot the triangle #
from matplotlib.patches import Polygon
axes.add_patch(Polygon(triangle, linewidth=1, edgecolor='k', facecolor='none'))
# Plot the points #
for i in range(count_points):
x = random.uniform(0, size_x)
y = random.uniform(0, size_y)
if point_in_triangle((x,y), triangle): pyplot.plot(x, y, '.g')
else: pyplot.plot(x, y, '.b')
# Save it #
figure.savefig("point_in_triangle.pdf")
다음 그래픽 생성 :
점이 두 개의 인접한 삼각형의 공통 모서리에 정확히있는 성가신 모서리 조건이 있습니다. 점은 삼각형 둘 다 또는 삼각형 둘 다에있을 수 없습니다. 포인트를 할당하는 임의의 일관된 방법이 필요합니다. 예를 들어 점을 통과하는 수평선을 그립니다. 선이 오른쪽에있는 삼각형의 다른면과 교차하면 점이 삼각형 안에있는 것처럼 취급됩니다. 교차점이 왼쪽에 있으면 점이 외부에있는 것입니다.
점이있는 선이 가로이면 위 / 아래를 사용하십시오.
점이 여러 삼각형의 공통 정점에있는 경우, 중심이 가장 작은 각도를 이루는 삼각형을 사용하십시오.
더 재미있는 점 : 세 점은 직선 (0도) 일 수 있습니다 (예 : (0,0)-(0,10)-(0,5)). 삼각 분할 알고리즘에서 "귀"(0,10)는 기울어 져 있어야하며, "삼각형"은 직선의 축퇴 된 경우 생성됩니다.
점이 삼각형 내부 또는 외부 또는 삼각형의 팔에 있는지 여부를 결정하는 가장 간단한 개념입니다.
점의 결정은 결정 요인에 의해 삼각형 안에 있습니다.
가장 간단한 작업 코드 :
#-*- coding: utf-8 -*-
import numpy as np
tri_points = [(1,1),(2,3),(3,1)]
def pisinTri(point,tri_points):
Dx , Dy = point
A,B,C = tri_points
Ax, Ay = A
Bx, By = B
Cx, Cy = C
M1 = np.array([ [Dx - Bx, Dy - By, 0],
[Ax - Bx, Ay - By, 0],
[1 , 1 , 1]
])
M2 = np.array([ [Dx - Ax, Dy - Ay, 0],
[Cx - Ax, Cy - Ay, 0],
[1 , 1 , 1]
])
M3 = np.array([ [Dx - Cx, Dy - Cy, 0],
[Bx - Cx, By - Cy, 0],
[1 , 1 , 1]
])
M1 = np.linalg.det(M1)
M2 = np.linalg.det(M2)
M3 = np.linalg.det(M3)
print(M1,M2,M3)
if(M1 == 0 or M2 == 0 or M3 ==0):
print("Point: ",point," lies on the arms of Triangle")
elif((M1 > 0 and M2 > 0 and M3 > 0)or(M1 < 0 and M2 < 0 and M3 < 0)):
#if products is non 0 check if all of their sign is same
print("Point: ",point," lies inside the Triangle")
else:
print("Point: ",point," lies outside the Triangle")
print("Vertices of Triangle: ",tri_points)
points = [(0,0),(1,1),(2,3),(3,1),(2,2),(4,4),(1,0),(0,4)]
for c in points:
pisinTri(c,tri_points)
가장 쉬운 방법이며 모든 유형의 삼각형에서 작동하는 것은 단순히 P 점 A, B, C 점 각도의 각도를 결정하는 것입니다. 각도가 180.0 도보 다 크면 바깥쪽에 있고, 180.0이면 둘레에 있고 acos가 당신을 속이고 180.0보다 작 으면 안쪽에 있습니다 .http : // math-physics -psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html
솔직히 Simon P Steven의 대답 만큼 간단 하지만 그 방법을 사용하면 삼각형 가장자리의 점을 포함할지 여부를 확실하게 제어 할 수 없습니다.
내 접근 방식은 조금 다르지만 매우 기본적입니다. 다음 삼각형을 고려하십시오.
삼각형으로 포인트를 가지려면 3 가지 조건을 만족해야합니다
이 방법에서는 가장자리의 점을 개별적으로 포함하거나 제외하도록 모든 권한을 갖습니다. 따라서 점이 | AC |를 포함하여 삼각형에 있는지 확인할 수 있습니다. 예를 들어 가장자리.
JavaScript에서 내 솔루션은 다음과 같습니다.
function isInTriangle(t,p){
function isInBorder(a,b,c,p){
var m = (a.y - b.y) / (a.x - b.x); // calculate the slope
return Math.sign(p.y - m*p.x + m*a.x - a.y) === Math.sign(c.y - m*c.x + m*a.x - a.y);
}
function findAngle(a,b,c){ // calculate the C angle from 3 points.
var ca = Math.hypot(c.x-a.x, c.y-a.y), // ca edge length
cb = Math.hypot(c.x-b.x, c.y-b.y), // cb edge length
ab = Math.hypot(a.x-b.x, a.y-b.y); // ab edge length
return Math.acos((ca*ca + cb*cb - ab*ab) / (2*ca*cb)); // return the C angle
}
var pas = t.slice(1)
.map(tp => findAngle(p,tp,t[0])), // find the angle between (p,t[0]) with (t[1],t[0]) & (t[2],t[0])
ta = findAngle(t[1],t[2],t[0]);
return pas[0] < ta && pas[1] < ta && isInBorder(t[1],t[2],t[0],p);
}
var triangle = [{x:3, y:4},{x:10, y:8},{x:6, y:10}],
point1 = {x:3, y:9},
point2 = {x:7, y:9};
console.log(isInTriangle(triangle,point1));
console.log(isInTriangle(triangle,point2));
bool isInside( float x, float y, float x1, float y1, float x2, float y2, float x3, float y3 ) {
float l1 = (x-x1)*(y3-y1) - (x3-x1)*(y-y1),
l2 = (x-x2)*(y1-y2) - (x1-x2)*(y-y2),
l3 = (x-x3)*(y2-y3) - (x2-x3)*(y-y3);
return (l1>0 && l2>0 && l3>0) || (l1<0 && l2<0 && l3<0);
}
이보다 더 효율적일 수는 없습니다! 삼각형의 각 변은 독립적 인 위치와 방향을 가질 수 있으므로 세 번의 계산이 필요합니다. l1, l2 및 l3은 각각 2 곱셈을 포함해야합니다. l1, l2 및 l3이 알려지면 결과는 몇 가지 기본 비교와 부울 연산입니다.
JavaScript로 적응 한 고성능 코드 (아래 기사) :
function pointInTriangle (p, p0, p1, p2) {
return (((p1.y - p0.y) * (p.x - p0.x) - (p1.x - p0.x) * (p.y - p0.y)) | ((p2.y - p1.y) * (p.x - p1.x) - (p2.x - p1.x) * (p.y - p1.y)) | ((p0.y - p2.y) * (p.x - p2.x) - (p0.x - p2.x) * (p.y - p2.y))) >= 0;
}
pointInTriangle(p, p0, p1, p2)
-반 시계 방향 삼각형pointInTriangle(p, p0, p1, p2)
-시계 방향 삼각형에서 봐 jsFiddle은 (성능 시험 포함), 별도의 기능에 체크가 감기입니다. 또는 아래의 "Run code snippet"을 누르십시오
var ctx = $("canvas")[0].getContext("2d");
var W = 500;
var H = 500;
var point = { x: W / 2, y: H / 2 };
var triangle = randomTriangle();
$("canvas").click(function(evt) {
point.x = evt.pageX - $(this).offset().left;
point.y = evt.pageY - $(this).offset().top;
test();
});
$("canvas").dblclick(function(evt) {
triangle = randomTriangle();
test();
});
document.querySelector('#performance').addEventListener('click', _testPerformance);
test();
function test() {
var result = checkClockwise(triangle.a, triangle.b, triangle.c) ? pointInTriangle(point, triangle.a, triangle.c, triangle.b) : pointInTriangle(point, triangle.a, triangle.b, triangle.c);
var info = "point = (" + point.x + "," + point.y + ")\n";
info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";
info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";
info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";
info += "result = " + (result ? "true" : "false");
$("#result").text(info);
render();
}
function _testPerformance () {
var px = [], py = [], p0x = [], p0y = [], p1x = [], p1y = [], p2x = [], p2y = [], p = [], p0 = [], p1 = [], p2 = [];
for(var i = 0; i < 1000000; i++) {
p[i] = {x: Math.random() * 100, y: Math.random() * 100};
p0[i] = {x: Math.random() * 100, y: Math.random() * 100};
p1[i] = {x: Math.random() * 100, y: Math.random() * 100};
p2[i] = {x: Math.random() * 100, y: Math.random() * 100};
}
console.time('optimal: pointInTriangle');
for(var i = 0; i < 1000000; i++) {
pointInTriangle(p[i], p0[i], p1[i], p2[i]);
}
console.timeEnd('optimal: pointInTriangle');
console.time('original: ptInTriangle');
for(var i = 0; i < 1000000; i++) {
ptInTriangle(p[i], p0[i], p1[i], p2[i]);
}
console.timeEnd('original: ptInTriangle');
}
function pointInTriangle (p, p0, p1, p2) {
return (((p1.y - p0.y) * (p.x - p0.x) - (p1.x - p0.x) * (p.y - p0.y)) | ((p2.y - p1.y) * (p.x - p1.x) - (p2.x - p1.x) * (p.y - p1.y)) | ((p0.y - p2.y) * (p.x - p2.x) - (p0.x - p2.x) * (p.y - p2.y))) >= 0;
}
function ptInTriangle(p, p0, p1, p2) {
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);
if (s <= 0 || t <= 0) return false;
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return (s + t) < A;
}
function render() {
ctx.fillStyle = "#CCC";
ctx.fillRect(0, 0, 500, 500);
drawTriangle(triangle.a, triangle.b, triangle.c);
drawPoint(point);
}
function checkClockwise(p0, p1, p2) {
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return A > 0;
}
function drawTriangle(p0, p1, p2) {
ctx.fillStyle = "#999";
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px monospace";
ctx.fillText("1", p0.x, p0.y);
ctx.fillText("2", p1.x, p1.y);
ctx.fillText("3", p2.x, p2.y);
}
function drawPoint(p) {
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomTriangle() {
return {
a: { x: rand(0, W), y: rand(0, H) },
b: { x: rand(0, W), y: rand(0, H) },
c: { x: rand(0, W), y: rand(0, H) }
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="performance">Run performance test (open console)</button>
<pre>Click: place the point.
Double click: random triangle.</pre>
<pre id="result"></pre>
<canvas width="500" height="500"></canvas>
이것에 의해 영감을 : http://www.phatcode.net/articles.php?id=459
bool point2Dtriangle(double e,double f, double a,double b,double c, double g,double h,double i, double v, double w){
/* inputs: e=point.x, f=point.y
a=triangle.Ax, b=triangle.Bx, c=triangle.Cx
g=triangle.Ay, h=triangle.By, i=triangle.Cy */
v = 1 - (f * (b - c) + h * (c - e) + i * (e - b)) / (g * (b - c) + h * (c - a) + i * (a - b));
w = (f * (a - b) + g * (b - e) + h * (e - a)) / (g * (b - c) + h * (c - a) + i * (a - b));
if (*v > -0.0 && *v < 1.0000001 && *w > -0.0 && *w < *v) return true;//is inside
else return false;//is outside
return 0;
}
Barycentric에서 변환 된 거의 완벽한 직교 좌표는 * v (x) 및 * w (y) doubles로 내보내집니다. 두 가지 내보내기 복식에는 모든 경우에 * 문자가 있어야합니다. * v 및 * w 코드는 사각형의 다른 삼각형에도 사용할 수 있습니다. 이에 의해 부호있는 abcd 쿼드로부터 삼각형 abc만이 기록되었다.
A---B
|..\\.o|
|....\\.|
D---C
o 점은 ABC 삼각형 안에 있으며 두 번째 삼각형으로 테스트하려면이 기능을 CDA 방향으로 호출하십시오. 결과는 사각형 이후 *v=1-*v;
와 *w=1-*w;
사각형에 대해 정확해야합니다
삼각형이 시계 방향이 될 것임을 확신 할 때 "제어 가능한 환경"에서 삼각형 확인의 포인트가 필요했습니다. 그래서 Perro Azul 의 jsfiddle을 가져 와서 그러한 경우 에 coproc이 제안한대로 수정했습니다 . 또한 서로를 취소하기 때문에 중복 0.5 및 2 곱셈을 제거했습니다.
http://jsfiddle.net/dog_funtom/H7D7g/
var ctx = $("canvas")[0].getContext("2d");
var W = 500;
var H = 500;
var point = {
x: W / 2,
y: H / 2
};
var triangle = randomTriangle();
$("canvas").click(function (evt) {
point.x = evt.pageX - $(this).offset().left;
point.y = evt.pageY - $(this).offset().top;
test();
});
$("canvas").dblclick(function (evt) {
triangle = randomTriangle();
test();
});
test();
function test() {
var result = ptInTriangle(point, triangle.a, triangle.b, triangle.c);
var info = "point = (" + point.x + "," + point.y + ")\n";
info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";
info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";
info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";
info += "result = " + (result ? "true" : "false");
$("#result").text(info);
render();
}
function ptInTriangle(p, p0, p1, p2) {
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);
if (s <= 0 || t <= 0) return false;
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return (s + t) < A;
}
function checkClockwise(p0, p1, p2) {
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return A > 0;
}
function render() {
ctx.fillStyle = "#CCC";
ctx.fillRect(0, 0, 500, 500);
drawTriangle(triangle.a, triangle.b, triangle.c);
drawPoint(point);
}
function drawTriangle(p0, p1, p2) {
ctx.fillStyle = "#999";
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px monospace";
ctx.fillText("1", p0.x, p0.y);
ctx.fillText("2", p1.x, p1.y);
ctx.fillText("3", p2.x, p2.y);
}
function drawPoint(p) {
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomTriangle() {
while (true) {
var result = {
a: {
x: rand(0, W),
y: rand(0, H)
},
b: {
x: rand(0, W),
y: rand(0, H)
},
c: {
x: rand(0, W),
y: rand(0, H)
}
};
if (checkClockwise(result.a, result.b, result.c)) return result;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<pre>Click: place the point.
Double click: random triangle.</pre>
<pre id="result"></pre>
<canvas width="500" height="500"></canvas>
Unity의 C # 코드는 다음과 같습니다.
public static bool IsPointInClockwiseTriangle(Vector2 p, Vector2 p0, Vector2 p1, Vector2 p2)
{
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);
if (s <= 0 || t <= 0)
return false;
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return (s + t) < A;
}