Bir noktanın bir üçgen içinde olup olmadığını belirlemenin kolay bir yolu var mı? 2D değil, 2D.
Bir noktanın bir üçgen içinde olup olmadığını belirlemenin kolay bir yolu var mı? 2D değil, 2D.
Yanıtlar:
Genel olarak, en basit (ve oldukça optimal) algoritma, noktanın kenarları tarafından oluşturulan yarım düzlemin hangi tarafında olduğunu kontrol eder.
İşte bu konuda GameDev'deki performans sorunları da dahil olmak üzere bazı yüksek kaliteli bilgiler .
İşte başlamanız için bazı kodlar:
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);
}
Aşağıdaki denklem sistemini çözün:
p = p0 + (p1 - p0) * s + (p2 - p0) * t
Nokta p
ise üçgenin içinde olduğunu 0 <= s <= 1
ve 0 <= t <= 1
ve s + t <= 1
.
s
, t
ve noktanın barycentric koordinatları1 - s - t
olarak adlandırılır .p
s + t <= 1
ima eder s <= 1
ve t <= 1
eğer s >= 0
ve t >= 0
.
Andreas Brinck'e katılıyorum , barycentric koordinatlar bu görev için çok uygun. Her seferinde bir denklem sistemini çözmeye gerek olmadığını unutmayın: sadece analitik çözümü değerlendirin. Andreas'ın notasyonunu kullanarak çözüm:
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
üçgenin (imzalı) alanı nerede :
Area = 0.5 *(-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y);
Sadece değerlendirmek s
, t
ve 1-s-t
. Noktap
Sadece üçgenin pozitif olması durumunda üçgenin içindedir.
DÜZENLEME: Alan için yukarıdaki ifadenin üçgen düğüm numaralandırmasının saat yönünün tersine olduğunu varsaydığını unutmayın. Numaralandırma saat yönünde ise, bu ifade negatif bir alana (ancak doğru büyüklükte) dönecektir. Testin kendisi ( s>0 && t>0 && 1-s-t>0
) numaralandırmanın yönüne bağlı değildir, çünkü yukarıdaki ifadeler ile çarpılır1/(2*Area)
üçgenler aynı zamanda üçgen düğüm yönü değişirse işareti de değiştirir.
Düzenleme 2: daha iyi bir hesaplama verimliliği için, bkz coproc üçgen düğümleri (saat yönünde ya da saat yönünün tersine) oryantasyonu ile, daha önce bölme biliniyorsa bu noktayı yapan aşağıdaki 'yorumunu ( 2*Area
için ifadelerde s
ve t
olabilmektedir kaçınılması). Andreas Brinck'in cevabı altındaki yorumlarda Perro Azul'un jsfiddle-koduna da bakınız .
2*Area
, yani hesaplanarak s´=2*|Area|*s
ve t´=2*|Area|*t
(noktaların yönü - saat yönünde veya saat yönünün tersine - bilinmiyorsa) bilinmeyebilir, Area
elbette işaretinin kontrol edilmesi gerekir, ancak aksi halde belki de hesaplanması gerekir), çünkü kontrol için kontrol s>0
etmek yeterlidir s´>0
. Ve kontrol 1-s-t>0
etmek yerine kontrol etmek yeterlidir s´+t´<2*|Area|
.
p0->p1->p2
olan saat yönünün tersi olarak Kartezyen (genellikle saat içinde ekran koordinatları ), Area
bu şekilde hesaplanan olumlu olacaktır.
Google ile son bir denemeden ve bu sayfayı bulmadan önce bu kodu yazdım, bu yüzden paylaşacağımı düşündüm. Temel olarak Kisielewicz cevabının optimize edilmiş bir versiyonudur. Ben de Barycentric yöntemine baktım ama Wikipedia makalesine bakıldığında nasıl daha verimli olduğunu görmekte zorlanıyorum (daha derin bir eşdeğerlik olduğunu tahmin ediyorum). Her neyse, bu algoritma bölme kullanmama avantajına sahiptir; potansiyel bir problem, yönlendirmeye bağlı olarak kenar algılamanın davranışıdır.
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;
}
Kelime ile fikir şudur: AB ve AC hatlarının her ikisinin solunda mı yoksa sağında mı? Eğer doğruysa, içeride olamaz. Yanlışsa, en azından durumu karşılayan "konilerin" içindedir. Şimdi, bir trigon (üçgen) içindeki bir noktanın AB'nin BC (ve ayrıca CA) ile aynı tarafında olması gerektiğini bildiğimizden, bunların farklı olup olmadığını kontrol ediyoruz. Eğer öyleyse, s muhtemelen içeride olamaz, aksi takdirde s içeride olmalıdır.
Hesaplamalardaki bazı anahtar kelimeler, çizgi yarı düzlemleri ve belirleyicidir (2x2 çapraz ürün). Belki de daha pedagojik bir yol muhtemelen AB, BC ve CA hatlarının her biriyle aynı tarafa (sol veya sağ) doğru olan bir nokta olarak düşünmektir. Ancak yukarıdaki yol bazı optimizasyonlar için daha uygun görünüyordu.
Andreasdr ve Perro Azul tarafından yayınlanan barycentric yöntemin C # sürümü. Karşılıklı işaretler varsa s
ve t
varsa , alan hesaplamasından kaçınılabilir . Oldukça kapsamlı bir birim testi ile doğru davranışı doğruladım.
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);
}
[ değiştir ]
@Pierre tarafından önerilen değişikliği kabul etti; Yorumlara bakınız
Barycentric yöntemin Java sürümü:
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;
}
Yukarıdaki kod, taşma olmadığını varsayarak tamsayılarla doğru şekilde çalışacaktır. Ayrıca saat yönünde ve saat yönünün tersine üçgenlerle çalışır. Doğrusal üçgenlerle çalışmaz (ancak det == 0'ı test ederek bunu kontrol edebilirsiniz).
Aynı üçgenle farklı noktaları test edecekseniz, barycentric versiyon en hızlısıdır.
Barycentric versiyon 3 üçgen noktasında simetrik değildir, bu nedenle kayan nokta yuvarlama hataları nedeniyle Kornel Kisielewicz'in kenar yarım düzlem versiyonundan daha az tutarlı olması muhtemeldir.
Kredi: Vikipedi'nin barycentric koordinatlar hakkındaki yazısından yukarıdaki kodu yaptım.
Basit bir yol:
noktayı üçgenin üç köşesinin her birine bağlayan vektörleri bulun ve bu vektörler arasındaki açıları toplayın. Açıların toplamı 2 * pi ise nokta üçgenin içindedir.
Alternatifleri açıklayan iki iyi site:
Barikat merkezli koordinatlara ( Andreas Brinck tarafından işaret edilen ) analitik çözümü kullanarak ve:
"Pahalı" işlemlerin sayısı en aza indirilebilir:
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;
}
Kod Perro Azul jsfiddle içine yapıştırılabilir veya aşağıdaki "Kod snippet'ini çalıştır" ı tıklayarak deneyebilirsiniz
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>
Giden:
Bu, Kornel Kisielewicz çözümüyle (25 hatırlama , 1 depolama, 15 çıkarma, 6 çarpma, 5 karşılaştırma) oldukça iyi bir şekilde karşılaştırılır ve saat yönünde / saat yönünün tersine algılama gerekiyorsa daha iyi olabilir (6 hatırlama alır, 1 toplama, 2 çıkarma) , Rhgb ile işaret edildiği gibi, analitik çözelti belirleyicisini kullanarak kendi içinde 2 çarpma ve 1 karşılaştırma ).
Yaptığım üç yüz normalini önceden hesaplamak,
3D yan vektörün ve yüz normal vektörünün çapraz ürünü.
bileşenleri değiştirerek ve birisini reddederek 2D olarak,
daha sonra herhangi bir taraf için iç / dış taraf normal bir yan ürün ve noktadan noktaya vektör, işareti değiştirmek zaman. Diğer iki (veya daha fazla) taraf için tekrarlayın.
Yararları:
aynı üçgen üzerinde çok noktalı test için çok fazla önceden hesaplanmıştır.
iç noktadan daha fazla dış vakaların erken reddi. (ayrıca nokta dağılımı bir tarafa ağırlık verilmişse, önce o tarafı test edebilir.)
İşte verimli bir Python uygulaması:
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))
ve bir örnek çıktı:
Hız arıyorsanız, size yardımcı olabilecek bir prosedür var.
Üçgen köşeleri koordinatlarına göre sıralayın. Bu en kötü üç karşılaştırmayı gerektirir. Y0, Y1, Y2 üç sıralı değer olsun. İçinden üç ufuk çizgisi çizerek uçağı iki yarım düzleme ve iki plakaya ayırırsınız. Y sorgu noktasının koordinatı olsun.
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
İki karşılaştırmaya daha mal olur. Gördüğünüz gibi, "sınırlayıcı levha" dışındaki noktalar için hızlı bir şekilde reddediliyor.
İsteğe bağlı olarak, solda ve sağda ( X <= X0' or X >= X2'
) hızlı ret için apseler üzerinde bir test yapabilirsiniz . Bu, aynı zamanda hızlı bir sınırlama kutusu testi uygulayacaktır, ancak apseleri de sıralamanız gerekir.
Sonunda, verilen noktanın işaretini, ilgili levhayı (üst veya alt) sınırlayan üçgenin iki tarafına göre hesaplamanız gerekecektir. Test şu şekildedir:
((X - Xi) * (Y - Yj) > (X - Xi) * (Y - Yj)) == ((X - Xi) * (Y - Yk) > (X - Xi) * (Y - Yk))
i, j, k
Kombinasyonların tam tartışması ( sıralamanın sonucuna bağlı olarak altı tane vardır) bu cevabın kapsamı dışındadır ve "okuyucuya bir egzersiz olarak bırakılmıştır"; verimlilik için bunlar sabit kodlanmış olmalıdır.
Bu çözümün karmaşık olduğunu düşünüyorsanız, sınırlayıcı kutu testinin başarısız olması durumunda temel olarak basit karşılaştırmalar (bazıları önceden hesaplanabilecek) artı 6 çıkarma ve 4 çarpım içerdiğini gözlemleyin. En kötü durumda test sonucunu iki tarafla karşılaştırmaktan kaçınamayacağınız gibi yenmek zordur (diğer cevaplardaki hiçbir yöntemin daha düşük bir maliyeti yoktur, bazıları 15 çıkarma ve 6 çarpma, bazen bölünmeler gibi daha kötü hale getirir).
GÜNCELLEME: Kesme dönüşümü ile daha hızlı
Yukarıda açıklandığı gibi, üç köşe koordinatı tarafından sınırlanan dört yatay banttan birinin içindeki noktayı, iki karşılaştırma kullanarak hızlı bir şekilde bulabilirsiniz.
Sınırlama kutusunun (noktalı çizgiler) durumunu kontrol etmek için isteğe bağlı olarak bir veya iki ekstra X testi yapabilirsiniz.
Sonra verilen "kesme" dönüşümünü göz önünde bulundurun, en yüksek kenarın eğimi X'= X - m Y, Y' = Y
nerede . Bu dönüşüm üçgenin bu tarafını dikey yapacaktır. Orta yatayın hangi tarafında olduğunuzu bildiğiniz için, işareti üçgenin tek bir tarafına göre test etmek yeterlidir.m
DX/DY
Eğimi m
, X'
makaslanmış üçgen köşeleri ve kenar denklemlerinin katsayıları için önceden hesapladığınızı varsayarak X = m Y + p
, en kötü durumda ihtiyacınız olacak
X' = X - m Y
;X >< m' Y + p'
makaslanmış üçgenin ilgili tarafına karşı bir işaret testi .Üç köşenin koordinatlarını ve belirli noktanın koordinatlarını biliyorsanız, tüm üçgenin alanını elde edebilirsiniz. Daha sonra, üç üçgen parçanın alanını hesaplayın (bir nokta verilen nokta ve diğer ikisi üçgenin herhangi iki köşesidir). Böylece, üç üçgen parçasının alanını alacaksınız. Bu alanların toplamı (daha önce aldığınız) toplam alana eşitse, nokta üçgenin içinde olmalıdır. Aksi takdirde, nokta üçgenin içinde değildir. Bu çalışmalı. Herhangi bir sorun varsa bana bildirin. Teşekkür ederim.
Python'daki diğer işlev , Geliştirici yönteminden daha hızlı (en azından benim için) ve Cédric Dufour çözümünden ilham aldı :
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 )
Şunlarla test edebilirsiniz:
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')
Çizilmesi çok zaman alıyor, ancak bu ızgara Geliştirici kodunun 0.0844349861145 saniyesine karşı 0.0195319652557 saniyede test ediliyor .
Son olarak kod yorumu:
# 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])
ve true
yanlış olmasına rağmen geri dönecek
JS yanıtı olmadığından,
Saat Yönü ve Saatin Tersi çözümü:
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
}
EDIT: det hesaplama ( cy - ay
yerine cx - ax
) için bir yazım hatası vardı , bu düzeltildi.
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>
Burada yukarıda açıklananla aynı yöntemi kullanıyorum: sırasıyla AB, BC, CA her satır "aynı" tarafında ise bir nokta ABC içinde.
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
) kullanırsanız, bu üçgen sargı sırasını belirlemek içindir, böylece yöntem CW ve CCW üçgenleriyle çalışır (bkz. jsFiddle).
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
bunun yerine let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
bu sabit, raporlama için teşekkürler
Andreas'ın verdiği barycentric koordinatlar çözümünü açıklamak için basit bir vektör matematiği kullanmak istiyorum, anlamak daha kolay olacak.
(1-s) | v0v2 | / | v0v2 | = tp | v0v1 | / | v0v1 |
1 - s = tp, sonra 1 = s + tp alırız. Çift çizgi çizgisinde 1 <s + t olan herhangi bir t> tp varsa, vektör üçgenin dışında, herhangi bir t <= tp ise, 1> = s + t ise tek çizgi çizgisinde, vektör üçgen içinde.
Daha sonra [0, 1] 'de herhangi bir s verdiysek, karşılık gelen t, üçgen içindeki vektör için 1> = s + t değerine uymalıdır.
Sonunda v = s * v02 + t * v01 olsun, v üçgenin içinde, s, t, s + t koşulu [0, 1] 'e ait. Sonra noktaya çevir, biz var
p - p0 = s * (p1 - p0) + t * (p2 - p0), [0, 1] 'de s, t, s + t ile
Andreas'ın p = p0 + s * (p1 - p0) + t * (p2 - p0) denklem sistemini çözme çözümü ile aynıdır, s, t, s + t [0, 1] 'e aittir.
İşte python'da verimli, belgelenmiş ve üç birim test içeren bir çözüm. Profesyonel kalitede ve projenize olduğu gibi bir modül şeklinde bırakılmaya hazır.
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)
Yukarıdaki algoritmanın geçerliliğini doğrulamak için isteğe bağlı ek bir grafik testi vardır:
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")
Aşağıdaki grafiği üretmek:
Bir noktanın tam olarak bitişik iki üçgenin ortak kenarında olduğu sinir bozucu kenar koşulları vardır. Nokta, her iki üçgende veya üçgenlerin hiçbirinde olamaz. Noktayı atamanın keyfi ancak tutarlı bir yoluna ihtiyacınız vardır. Örneğin, nokta boyunca yatay bir çizgi çizin. Çizgi sağ taraftaki üçgenin diğer tarafıyla kesişirse, nokta üçgenin içindeymiş gibi işlenir. Kavşak solda ise, nokta dışarıdadır.
Noktanın bulunduğu çizgi yataysa, yukarı / aşağı kullanın.
Nokta, birden çok üçgenin ortak tepe noktasındaysa, merkezi noktanın en küçük açıyı oluşturduğu üçgeni kullanın.
Daha eğlenceli: Üç nokta düz bir çizgide olabilir (sıfır derece), örneğin (0,0) - (0,10) - (0,5). Bir üçgenleme algoritmasında, "kulak" (0,10) kapatılmalı, üretilen "üçgen" düz bir çizginin dejenere olgusudur.
Bir noktanın üçgenin içinde mi yoksa dışında mı yoksa bir üçgenin kolunda mı olduğunu belirlemek için en basit kavram budur.
Bir noktanın determinantlarla bir üçgen içinde belirlenmesi:
En basit çalışma kodu:
#-*- 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)
En kolay yol ve her türlü üçgenle çalışır, P noktası A, B, C nokta açılarının açılarını belirlemektir. Eğer açılardan herhangi biri 180.0 dereceden büyükse, o zaman dışarıda, eğer 180.0 ise çevrede ve acos sizi aldatıyorsa ve 180.0'dan azsa içeride. Http: // math- physics'i anlamak için bir göz atın -psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html
Dürüst olmak gerekirse, Simon P Steven'ın cevabı kadar basittir, ancak bu yaklaşımla, üçgenin kenarlarındaki noktaların dahil edilmesini isteyip istemediğiniz üzerinde katı bir kontrole sahip değilsiniz.
Benim yaklaşımım biraz farklı ama çok basit. Aşağıdaki üçgeni düşünün;
Üçgendeki noktayı elde etmek için 3 koşulu yerine getirmeliyiz
Bu yöntemde, kenarlardaki noktayı ayrı ayrı dahil etmek veya hariç tutmak için tam kontrole sahipsiniz. Üçgen içinde sadece | AC | örneğin kenar.
Bu yüzden JavaScript'teki çözümüm şöyle olacak;
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);
}
Bundan daha verimli olamaz! Bir üçgenin her bir tarafı bağımsız pozisyona ve yöne sahip olabilir, bu nedenle üç hesaplama vardır: l1, l2 ve l3, her biri 2 çarpımı içeren kesinlikle gereklidir. L1, l2 ve l3 bilindikten sonra, sonuç sadece birkaç temel karşılaştırma ve boole işlemidir.
JavaScript'te uyarladığım yüksek performanslı kod (aşağıdaki makale):
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)
- saat yönünün tersine üçgenler içinpointInTriangle(p, p0, p1, p2)
- saat yönünde üçgenler içinJSFiddle'a bakın (performans testi dahil), ayrı bir işlevde sargı kontrolü de vardır. Veya aşağıdaki "Kod snippet'ini çalıştır" a basın
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>
Bundan ilham alan: 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;
}
Bariyer merkezden dönüştürülen neredeyse mükemmel Kartezyen koordinatlar * v (x) ve * w (y) iki katına çıkarılır. Her iki dışa aktarma iki katının her durumda önceden * karakteri olmalıdır, büyük olasılıkla: * v ve * w Kod, bir dörtgenin diğer üçgeni için de kullanılabilir. Burada imzalı saat yönünde abcd dörtlü sadece abc üçgeni yazdı.
A---B
|..\\.o|
|....\\.|
D---C
o noktası, ikinci üçgenle test için ABC üçgeninin içindedir bu işlevi CDA yönüne çağırır ve sonuçlar dörtgenden sonra *v=1-*v;
ve *w=1-*w;
dörtgen için doğru olmalıdır
Üçgenlerin saat yönünde olacağından kesinlikle emin olduğunuzda "kontrol edilebilir ortamda" üçgen kontrolünde noktaya ihtiyacım vardı. Böylece, Perro Azul'un jsfiddle'ını aldım ve bu gibi durumlar için coproc tarafından önerildiği gibi değiştirdim ; Ayrıca, birbirlerini iptal ettikleri için fazladan 0,5 ve 2 çarpımı da kaldırmışlardır.
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>
İşte Unity için eşdeğer C # kodu:
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;
}
Üçgenin (x1, y1), (x2, y2), (x3, y3) köşeleri tarafından oluşturulan alanın pozitif olup olmadığını kontrol etmenin en kolay yollarından biri.
Alan formülle hesaplanabilir:
1/2 [x1 (y2 – y3) + x2 (y3 – y1) + x3 (y1 – y2)]
veya python kodu şu şekilde yazılabilir:
def triangleornot(p1,p2,p3):
return (1/ 2) [p1[0](p2[1]–p3[1]) + p2[0] (p3[1]–p1[1]) + p3[0] (p1[0]–p2[0])]