点が三角形の内側にあるかどうかを判断する簡単な方法はありますか? 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
0 <= s <= 1
と0 <= t <= 1
とs + t <= 1
の場合、点p
は三角形の内側にあります。
s
、t
および1 - s - t
は、ポイントp
の 重心座標 と呼ばれます。
私は同意します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のコメントを参照してください(三角形ノードの向き(時計回りまたは反時計回り)が事前にわかっている場合は、 s
およびt
の式の中の2*Area
は避けることができます。下記のコメントのPerro Azulのjsfiddle-codeも参照してくださいAndreas Brinckの答え。
私はこのコードをグーグルとの最後の試みとこのページの発見の前に書いたので、私はそれを共有しようと思った。それは基本的にKisielewicz答えの最適化版です。私はBarycentric法についても調べましたが、ウィキペディアの記事から判断すると、それがより効率的であるかどうかを判断するのに苦労しています(より深い等価性があると思います)。とにかく、このアルゴリズムには除算を使用しないという利点があります。潜在的な問題は、向きに応じたエッジ検出の動作です。
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;
}
つまり、考え方は次のとおりです。ポイントsは、ラインABとラインACの両方の左側または右側のどちらにありますか。本当なら、それは中にあることはできません。偽の場合、それは少なくとも条件を満たす "コーン"の内側にあります。三角(三角形)の内側の点は、BC(およびCA)と同じABの側にある必要があることがわかったので、それらが異なるかどうかを確認します。もしそうであれば、sはおそらく内側にあることはできません。
計算におけるいくつかのキーワードは線半平面と行列式(2×2外積)です。おそらく、もっと教育的な方法は、それがAB、BC、CAの各行の同じ側(左または右)にある場合は、それを内側の点と考えることです。しかし、上記の方法は、いくつかの最適化により適しているように見えました。
C# andreasdrとPerro Azulによって投稿された重心法のバージョン。 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);
}
[編集]
@Pierreによる修正提案を受け入れました。コメントを見る
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をテストすることでこれを確認できます)。
あなたが同じ三角形で異なる点をテストするつもりならば、重心バージョンは最速です。
重心バージョンは、3つの三角形の点で対称的ではないため、浮動小数点丸め誤差のため、Kornel KisielewiczのEdgeハーフプレーンバージョンよりも一貫性が低くなる可能性があります。
クレジット:私は重心座標に関するウィキペディアの記事から上記のコードを作りました。
重心座標 (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 Azuljsfiddle )
につながる:
これは、Kornel Kisielewicz解(25回の呼び戻し、1回の記憶、15回の減算、6回の乗算、5回の比較)と非常によく似ています。 rhgbで指摘されているように、分析解の行列式を使用して、6回の呼び出し、1回の加算、2回の減算、2回の乗算、および1回の比較が必要です。
私がすることは三つの法線法線を事前計算することです、
側面ベクトルと面の法線ベクトルの外積による3D。
2Dでは、単純にコンポーネントを交換し、コンポーネントを無効にすることによって、
それから任意の一辺の内側/外側は辺の法線と点ベクトルへの頂点の内積が符号を変えるときです。他の2つ(またはそれ以上)の辺についても繰り返します。
利点:
同じ三角形上での多点テストには、非常に多くの量が事前計算されています。
内側の点よりも外側の一般的な場合の早期拒絶。 (また、点分布が片側に重み付けされている場合は、その側を先にテストできます。)
これが効率的な Python の実装です。
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))
そして出力例:
3つの頂点の座標と特定の点の座標がわかっていれば、完全な三角形の面積を求めることができます。その後、3つの三角形セグメントの面積を計算します(1つの点は与えられた点で、他の2つは三角形の任意の2つの頂点です)。したがって、3つの三角形セグメントの面積がわかります。これらの面積の合計が(以前に得た)合計面積と等しい場合、その点は三角形の内側にあるはずです。そうでなければ、点は三角形の内側にはありません。これでうまくいくはずです。何か問題があれば、教えてください。ありがとうございました。
あなたがスピードを探しているなら、ここにあなたを助けるかもしれない手順があります。
三角形の頂点を縦座標に並べ替えます。これは最低でも3つの比較が必要です。 Y0、Y1、Y2を3つのソート値とします。それらを通して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
さらに2回の比較が必要です。お分かりのように、「境界スラブ」の外側の点では素早い拒否が達成されます。
必要に応じて、左右にクイック拒否のテストを横軸に指定できます(X <= X0' or X >= X2'
)。これは同時にクイックバウンディングボックステストを実装しますが、横座標もソートする必要があります。
最終的には、関連するスラブ(上または下)を区切る三角形の2つの辺に関して与えられた点の符号を計算する必要があります。テストの形式は以下のとおりです。
((X - Xi) * (Y - Yj) > (X - Xi) * (Y - Yj)) == ((X - Xi) * (Y - Yk) > (X - Xi) * (Y - Yk))
i, j, k
の組み合わせ(ソートの結果に基づいてそれらのうちの6つがあります)の完全な議論はこの回答の範囲外であり、「読者への課題として残されています」。効率のために、それらはハードコードされるべきです。
この解が複雑であると思うならば、それは主に単純な比較(そのうちのいくつかは事前計算することができます)に加えて6回の減算と境界ボックステストが失敗した場合の4回の乗算を含みます。テストポイントを両側と比較することを避けることができない最悪の場合のように後者のコストは打ち勝つことが困難です(他の答えの方法はより低いコストを持ちません、15引き算と6乗算、時には分割など)。
更新:シア変換で高速化
上で説明したように、2つの比較を使用して、3つの頂点座標で区切られた4つの水平バンドのうちの1つの内側にある点をすばやく見つけることができます。
境界ボックスの内側をチェックするために、オプションで1つまたは2つの追加のXテストを実行できます(点線)。
それからX'= X - m Y, Y' = Y
によって与えられる "シア"変換を考えてください。ここでm
は最も高いEdgeの傾きDX/DY
です。この変換は三角形のこの辺を垂直にします。そして、あなたはあなたが真ん中の水平のどちら側にいるかを知っているので、それは三角形の片側に関してサインをテストすることで十分です。
勾配m
、および剪断三角頂点のX'
と辺の方程式の係数をX = m Y + p
として事前計算したとすると、最悪の場合に必要になります。
X' = X - m Y
の計算X >< m' Y + p'
の符号を1回テストします。pythonの他の関数、Developer's method(少なくとも私にとっては)より速く、CédricDufour solutionに触発されたもの:
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.0195319652557秒でテストされますが、0.0844349861145秒の開発者コードです。
最後にコードのコメント:
# 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
ある点が2つの隣接する三角形の共通のエッジ上にあるという厄介なエッジ条件があります。点は両方の三角形の中にあってはならず、またどちらの三角形にもあってはなりません。あなたはポイントを割り当てるための恣意的だが一貫した方法が必要です。たとえば、点を通る水平線を引きます。線が右側の三角形の反対側の辺と交差する場合、その点は三角形の内側にあるかのように扱われます。交差点が左側にある場合、その点は外側にあります。
ポイントがある線が水平の場合は、上/下を使用してください。
点が複数の三角形の共通の頂点上にある場合は、その点を中心とする三角形が最も小さい角度を形成するようにします。
もっと楽しく:3つの点を直線(0度)にすることができます。例えば、(0,0) - (0,10) - (0,5)です。三角測量アルゴリズムでは、 "耳"(0,10)を切り落とさなければならず、生成された "三角形"は直線の縮退した場合です。
これは、効率的で文書化された3つのユニットテストを含むpythonのソリューションです。それはプロ級の品質で、そのままモジュールの形であなたのプロジェクトに組み込まれる準備ができています。
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 __== "__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")
次の図を作成します。
私はAndreasが与えた重心座標の解法を説明するために簡単なベクトル数学を使いたいだけなので、理解するのはずっと簡単でしょう。
(1-s)| v0v2 |/| v0v2 | = tp| v0v1 |/| v0v1 |
1 - s = tp、そして1 = s + tpとなります。二点鎖線上にある任意のt> tpである場合、ベクトルは三角形の外側にあり、任意のt <= tp、一点鎖線上にある場合は次のようになります。三角形の中.
それで、[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)、ここでs、t、s + tは[0、1]
これは、方程式システムp = p0 + s *(p1 - p0)+ t *(p2 - p0)を解くためのAndreasの解と同じです。ここで、s、t、s + tは[0、1]に属します。
三角形が時計回りになることが確実である場合は、「制御可能な環境」で三角形の点をチェックする必要がありました。それで、私はPerro Azulのjsfiddleを取り、coprocによって示唆されるようにそれを修正しました。また、0.5と2の冗長な乗算が削除されました。これらは互いにキャンセルされているためです。
http://jsfiddle.net/dog_funtom/H7D7g/
これは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;
}
正直なところ、それは Simon P Steven's answer と同じくらい簡単ですが、三角形のエッジ上の点を含めるかどうかをしっかりと制御することはできません。
私のアプローチは少し異なりますが、非常に基本的なものです。次の三角形を考えてください。
三角形の点を得るためには、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));
最も簡単な方法は、すべてのタイプの三角形で機能することです。単にP点の角度を決定することです。角度のいずれかが180.0度よりも大きい場合、それは外側です、180.0の場合、それは円周上にあり、acosがあなたに詐欺し、180.0未満の場合、それは内側です。理解のために見てください http: //math-physics-psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html
JSの答えがないので、
右回りと左回りソリューション:
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/
私はここで上記と同じ方法を使用しています。彼がそれぞれの線AB、BC、CAの「同じ」側にいる場合、点はABCの内側にあります。
one of the easiest ways to check if the area formed by the vertices of triangle
(x1,y1),(x2,y2),(x3,y3) is postive or not .
area can by calculated by the formula.
1/ 2 [x1(y2–y3) + x2 (y3–y1) + x3 (y1–y2)]
or python code can be written as:-
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])]
これは、点が三角形の内側にあるのか外側にあるのか、または三角形の腕の上にあるのかを判断するための最も簡単な概念です。 点の決定は行列式によるトリングルの内側です
最も簡単な作業コード: `
#-*- 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)
`
私が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(パフォーマンステストが含まれています)を見てください。また、別の関数でのワインディングチェックもあります http://jsfiddle.net/z7x0udf7/3/
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);
}
これ以上効率的にはなり得ません。三角形の各辺は独立した位置と向きを持つことができるので、3つの計算:l 1、l 2、l 3はそれぞれ2回の乗算を含むことで必要です。 l1、l2、およびl3がわかると、結果は基本的な比較がいくつか行われ、ブール演算がなくなります。