web-dev-qa-db-ja.com

点が2次元三角形内にあるかどうかを判断する方法

点が三角形の内側にあるかどうかを判断する簡単な方法はありますか? 3Dではなく2Dです。

229
ET 0.618

一般に、最も単純な(そして非常に最適な)アルゴリズムは、エッジによって作成されたハーフプレーンのどちら側にあるかを調べます。

ここにいくつかの高品質の情報があります 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);
}
239

次の方程式系を解きます。

p = p0 + (p1 - p0) * s + (p2 - p0) * t

0 <= s <= 10 <= t <= 1s + t <= 1の場合、点pは三角形の内側にあります。

stおよび1 - s - tは、ポイントp重心座標 と呼ばれます。

157
Andreas Brinck

私は同意します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);

st、および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の答え。

102
andreasdr

私はこのコードをグーグルとの最後の試みとこのページの発見の前に書いたので、私はそれを共有しようと思った。それは基本的に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の各行の同じ側(左または右)にある場合は、それを内側の点と考えることです。しかし、上記の方法は、いくつかの最適化により適しているように見えました。

39
John Bananas

C# andreasdrとPerro Azulによって投稿された重心法のバージョン。 stの符号が逆の場合は、面積計算を回避できることに注意してください。私はかなり徹底的な単体テストで正しい動作を検証しました。

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による修正提案を受け入れました。コメントを見る

25
Glenn Slayden

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ハーフプレーンバージョンよりも一貫性が低くなる可能性があります。

クレジット:私は重心座標に関するウィキペディアの記事から上記のコードを作りました。

11
Adam Gawne-Cain

簡単な方法は:

点を三角形の3つの頂点のそれぞれに接続するベクトルを見つけ、それらのベクトル間の角度を合計します。角度の合計が2 * piの場合、点は三角形の内側にあります。

選択肢を説明する2つの良いサイトは以下のとおりです。

blackpawn そして wolfram

10
Simon P Stevens

重心座標Andreas Brinckが指す)の解析解を使用して、

  • 括弧で囲まれた項に掛け算を分配しない
  • それらを格納することによって同じ項を数回計算することを避ける
  • 比較の削減(coprocThomas Edingが指摘するとおり)

「高価な」操作の数を最小限に抑えることができます。

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

につながる:

  • 変数 "リコール":30
  • 可変ストレージ:7
  • 追加:4
  • 減算:8
  • 乗算:6
  • 部門:なし
  • 比較:4

これは、Kornel Kisielewicz解(25回の呼び戻し、1回の記憶、15回の減算、6回の乗算、5回の比較)と非常によく似ています。 rhgbで指摘されているように、分析解の行列式を使用して、6回の呼び出し、1回の加算、2回の減算、2回の乗算、および1回の比較が必要です。

7
Cédric Dufour

私がすることは三つの法線法線を事前計算することです、

  • 側面ベクトルと面の法線ベクトルの外積による3D。

  • 2Dでは、単純にコンポーネントを交換し、コンポーネントを無効にすることによって、

それから任意の一辺の内側/外側は辺の法線と点ベクトルへの頂点の内積が符号を変えるときです。他の2つ(またはそれ以上)の辺についても繰り返します。

利点:

  • 同じ三角形上での多点テストには、非常に多くの量が事前計算されています。

  • 内側の点よりも外側の一般的な場合の早期拒絶。 (また、点分布が片側に重み付けされている場合は、その側を先にテストできます。)

5
psiman

これが効率的な 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))

そして出力例:

enter image description here

4
Developer

3つの頂点の座標と特定の点の座標がわかっていれば、完全な三角形の面積を求めることができます。その後、3つの三角形セグメントの面積を計算します(1つの点は与えられた点で、他の2つは三角形の任意の2つの頂点です)。したがって、3つの三角形セグメントの面積がわかります。これらの面積の合計が(以前に得た)合計面積と等しい場合、その点は三角形の内側にあるはずです。そうでなければ、点は三角形の内側にはありません。これでうまくいくはずです。何か問題があれば、教えてください。ありがとうございました。

3
ihayet

あなたがスピードを探しているなら、ここにあなたを助けるかもしれない手順があります。

三角形の頂点を縦座標に並べ替えます。これは最低でも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です。この変換は三角形のこの辺を垂直にします。そして、あなたはあなたが真ん中の水平のどちら側にいるかを知っているので、それは三角形の片側に関してサインをテストすることで十分です。

enter image description here

勾配m、および剪断三角頂点のX'と辺の方程式の係数をX = m Y + pとして事前計算したとすると、最悪の場合に必要になります。

  • 垂直分類の2つの縦座標比較。
  • 場合によっては、バウンディングボックスの棄却に対する1つまたは2つの横座標比較。
  • X' = X - m Yの計算
  • 剪断された三角形の横座標との1、2回の比較。
  • 剪断された三角形の該当する辺に対してX >< m' Y + p'の符号を1回テストします。
3
Yves Daoust

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
3
Ramiro R.C.

ある点が2つの隣接する三角形の共通のエッジ上にあるという厄介なエッジ条件があります。点は両方の三角形の中にあってはならず、またどちらの三角形にもあってはなりません。あなたはポイントを割り当てるための恣意的だが一貫した方法が必要です。たとえば、点を通る水平線を引きます。線が右側の三角形の反対側の辺と交差する場合、その点は三角形の内側にあるかのように扱われます。交差点が左側にある場合、その点は外側にあります。

ポイントがある線が水平の場合は、上/下を使用してください。

点が複数の三角形の共通の頂点上にある場合は、その点を中心とする三角形が最も小さい角度を形成するようにします。

もっと楽しく:3つの点を直線(0度)にすることができます。例えば、(0,0) - (0,10) - (0,5)です。三角測量アルゴリズムでは、 "耳"(0,10)を切り落とさなければならず、生成された "三角形"は直線の縮退した場合です。

1
Pierre

これは、効率的で文書化された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")

次の図を作成します。

Test the point_in_triangle function

1
xApple

私はAndreasが与えた重心座標の解法を説明するために簡単なベクトル数学を使いたいだけなので、理解するのはずっと簡単でしょう。

  1. 領域Aは、条件s> = 0およびt> = 0の状態で、s * v02 + t * v01で与えられる任意のベクトルとして定義されます。三角形v0、v1、v2の内側の任意の点は、領域Aの内側になければなりません。

enter image description here

  1. さらにsを制限し、tが[0、1]に属する場合。 s * v02 + t * v01のすべてのベクトルを含む領域Bが得られ、条件s、tは[0、1]に属します。エリアBの低い部分はTriangle v0、v1、v2のミラーです。エリアBの低い部分をさらに除外するために、sとtの特定の条件を与えることができる場合、問題が起こります。

enter image description here

  1. 値sを与え、tが[0、1]で変化しているとします。次の図では、点pはv1v2の端にあります。一点鎖線上にあるs * v02 + t * v01のすべてのベクトルは単純なベクトル和で表されます。 v1v2と破線の交差点pでは、次のようになります。

(1-s)| v0v2 |/| v0v2 | = tp| v0v1 |/| v0v1 |

1 - s = tp、そして1 = s + tpとなります。二点鎖線上にある任意のt> tpである場合、ベクトルは三角形の外側にあり、任意のt <= tp、一点鎖線上にある場合は次のようになります。三角形の中.

それで、[0、1]にsを与えた場合、三角形の内側のベクトルに対して、対応するtは1> = s + tを満たす必要があります。

enter image description here

したがって、最後に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]に属します。

1
Orup

三角形が時計回りになることが確実である場合は、「制御可能な環境」で三角形の点をチェックする必要がありました。それで、私は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;
}
0
Maxim Kamalov

正直なところ、それは Simon P Steven's answer と同じくらい簡単ですが、三角形のエッジ上の点を含めるかどうかをしっかりと制御することはできません。

私のアプローチは少し異なりますが、非常に基本的なものです。次の三角形を考えてください。

enter image description here

三角形の点を得るためには、3つの条件を満たす必要があります。

  1. ACEの角度(緑)はACBの角度(赤)より小さくなければなりません
  2. ECB角(青)はACB角(赤)より小さくなければなりません
  3. Xとyの値が| AB |の方程式に適用されると、点Eと点Cは同じ符号になります。ライン。

この方法では、エッジ上の点を個別に含めるか除外するかを完全に制御できます。そのため、| 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));
0
Redu

最も簡単な方法は、すべてのタイプの三角形で機能することです。単にP点の角度を決定することです。角度のいずれかが180.0度よりも大きい場合、それは外側です、180.0の場合、それは円周上にあり、acosがあなたに詐欺し、180.0未満の場合、それは内側です。理解のために見てください http: //math-physics-psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html

0
Bela Bessenyei

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/enter image description here

私はここで上記と同じ方法を使用しています。彼がそれぞれの線AB、BC、CAの「同じ」側にいる場合、点はABCの内側にあります。 triangle inclusion example

0
        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])]
0
ravi tanwar

これは、点が三角形の内側にあるのか外側にあるのか、または三角形の腕の上にあるのかを判断するための最も簡単な概念です。 点の決定は行列式によるトリングルの内側です

最も簡単な作業コード: `

#-*- 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)

`

0
Shaon Majumder

私が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/

これに触発された: http://www.phatcode.net/articles.php?id=459

0
Pawel
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がわかると、結果は基本的な比較がいくつか行われ、ブール演算がなくなります。

0
Ajay Anand