ポイントが2D三角形にあるかどうかを判断する方法は?[閉まっている]


258

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


15
トライアングルテストのポイントに関する完全な記事を書きました。これは、重心、パラメトリック、および内積に基づく方法を示しています。次に、ポイントが1つのエッジに正確にある場合に発生する精度の問題を扱います(例を示します)。最後に、ポイントからエッジへの距離に基づいた完全な新しいメソッドを公開します。totologic.blogspot.fr/2014/01/…お楽しみください!
ロジック


1
ここで説明する方法はすべて3D空間でも有効であることは注目に値します。それらの前に座標変換(および三角形の平面上の点の適切な投影)が必要です。三角形は2次元オブジェクトです。
andreasdr 2017年

巻線順序に依存しないソリューションの場合。これが実際のフィドルです:jsfiddle.net/ibowankenobi/oex3pzq2
ibrahim tanyalcin

2
この質問は、プログラミングではなく数学に関するものであり、意見に基づいている(何が「簡単」か)ため、私はこの質問を締めくくります。
TylerH

回答:


264

一般に、最も単純な(そして非常に最適な)アルゴリズムは、エッジによって作成された半平面のどちら側にポイントがあるかをチェックします。

パフォーマンスの問題を含む、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);
}

12
2Dでよく使用されます。重心座標は人々を混乱させる傾向があります。また、三角形の座標と点座標を考えると、重心を使用する効率についてはわかりません。
Kornel Kisielewicz、2010年

7
@Kornel重心バージョンは2Dでもより効率的です。ソリューションには、三角形が時計回りまたは反時計回りの順序で指定されているかどうかに応じて、三角形のエッジ上の正確な点について異なる結果を報告するという問題もあります。
Andreas Brinck、2010年

9
私の目的(私がこのサイトを見つけた理由)では、Kornel Kisielewiczによって提案された元の回答の方がはるかに効率的です。BYTEサイズ座標のLCDディスプレイと、整数乗算が非常に高速な命令であり、除算がはるかに遅い、非常に典型的なマイクロプロセッサを使用しています。除算がないため、数値の問題もはるかに小さくなります。すべての計算は正確です。ありがとう、リック

4
つまり、sign()関数は、半平面のどちら側(p2とp3の間の線で形成される)p1かを示します。
David Doria 2013年

1
頂点の順序(反時計回りなど)を想定している場合は、それらすべての行列式を常に計算する必要はありません。実際、最良のケースでは、1行列式で、ポイントが三角形の内側にないことがわかります。
16

176

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

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

とのp場合0 <= s <= 1、ポイントは三角形の内側に0 <= t <= 1ありs + t <= 1ます。

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


1
これは、半平面チェックよりも高速ですが、重心座標に慣れていない場合は、少し把握するのがおそらく難しいでしょう。
Daniel Rikowski、2010年

8
Kornelのメソッドに取るに足らない出口(実装されていない)を使用すると、Kornelは実際の出口よりもはるかに効率的です。実際にsとtを計算しようとすると、私の意味がわかります。

85
私はこれをテストしたかったので、@ andreasdrソリューションとcoprocコメントに依存してjsfiddleを作成しました:jsfiddle.net/PerroAZUL/zdaY8/1
urraka

5
最適化:をs + t <= 1意味しs <= 1t <= 1if s >= 0t >= 0
Thomas Eding、2014年

7
@Logicの投稿で提案された記事totologic.blogspot.fr/2014/01/…は、このソリューションの理解を深めるのに役立ちました
Flayn

112

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-tpすべて正の場合に限り、ポイントは三角形の内側にあります。

編集:エリアの上記の式は、三角形のノードの番号付けが反時計回りであることを前提としています。番号が時計回りの場合、この式は負の面積を返します(ただし、大きさは正しくなります)。s>0 && t>0 && 1-s-t>0ただし、テスト自体()は番号付けの方向には依存しません1/(2*Area)。これは、三角形のノードの向きが変わると、上記の式に乗算される符号も変わるためです。

EDIT 2:より良い計算効率のために、参照coprocを三角形ノード(時計回りまたは反時計回り)の方向をすることにより、予め分割知られている場合、その点になり以下のコメント(2*Areaの式にstすることができます避けてください)。Andreas Brinckの回答の下にあるコメントのPerro Azulのjsfiddle-code も参照してください。


6
それ方程式系を解くことです:)
Andreas Brinck 2013年

1
はい、私のポイントは、方程式系を解くことの計算コストに基づくあなたの方法の批判は根拠がないということです、それはそれがアルゴリズムの一部として行われる必要がないからです。
andreasdr 2013年

13
を除算しないことで効率を向上させることができます2*Area。つまり、計算しs´=2*|Area|*st´=2*|Area|*t(ポイントの向き(時計回りまたは反時計回り)がわからないArea場合は、もちろんの符号を確認する必要がありますが、それ以外の場合は、計算する必要があります)、チェックs>0するにはをチェックするだけで十分s´>0です。そして、チェック1-s-t>0する代わりに、チェックするだけで十分s´+t´<2*|Area|です。
coproc 2013

1
私があれば追加してもよいp0->p1->p2である反時計方向直交(通常は時計回りスクリーン座標)、Areaこの方法で算出は正となります。
rhgb 2014年

1
@ user2600366三角形の境界に沿ってp0-> p1-> p2-> p0のように移動すると、三角形の内部が常に右または常に左になります。前者の場合、番号付けは時計回りであり、後者の場合、反時計回りです。
andreasdr

47

私はこのコードをグーグルで最後に試してこのページを見つける前に書いたので、共有したいと思った。これは基本的にはKisielewicz回答の最適化バージョンです。私はバリセントリック法も調べましたが、ウィキペディアの記事から判断して、それがいかに効率的であるかを理解するのに苦労しています(同等性がより深いと思います)。とにかく、このアルゴリズムには除算を使用しないという利点があります。潜在的な問題は、向きに応じたエッジ検出の動作です。

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の両方の左側または右側にありますか?trueの場合、内部にすることはできません。falseの場合、少なくとも条件を満たす「コーン」内にあります。これで、三角形(三角形)内のポイントはABのBC(およびCA)と同じ側にある必要があることがわかったので、それらが異なるかどうかを確認します。もしそうなら、sはおそらく内部に存在できません。そうでなければ、sは内部になければなりません。

計算のいくつかのキーワードは、線の半平面と行列式(2x2の外積)です。おそらく、より教育的な方法は、AB、BC、CAの各線と同じ側(左または右)にある場合に限り、内部にある点と考えることでしょう。上記の方法は、いくつかの最適化に適しているようです。


2
このテストは、最初に提供されたテストよりも約140〜180%高速です(両方のおかげで:)。ここでコードを実行:paste.ubuntu.com/p/k5w7ywH4p8最適化を無効にしてnodejs v8エンジンを使用し、次の結果を得ました::w!node -p --minimal test1:114.852ms test2:64.330ms test1:115.650ms test2:63.491ms test1:117.671ms test2:65.353ms test1:119.146ms test2:63.871ms test1:118.271ms test1:118.670ms test2:63.352ms
サージマッケイ

@surgemcgeeなぜ最適化なしで実行するのですか?それでは、現実からそれが取り除かれているのではないでしょうか?
xuiqzy

@xuiqzyええと、私のプログラムには2つの異なるソリューションが含まれています。私はそれを行う最速の方法をまだ管理していません。おそらく、コメントは。これに関しては、私の完成作品を取り外して交換する必要があること
surgemcgee

33

andreasdrとPerro Azulによって投稿された重心法のC#バージョン。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による提案された変更を受け入れました。コメントを見る


末尾のifステートメントを使用したソリューションは、時計回りと反時計回りの三角形の点で機能します。
ルークデュパン

@LukeDupinコメントが理解できません。この回答は、提供された3ポイントの注文に対して投稿されたとおりに機能します。
Glenn Slayden

12

重心メソッドの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のエッジ半平面バージョンよりも一貫性が低くなる可能性があります。

クレジット:重心座標に関するWikipediaの記事から上記のコードを作成しました。


いいね!データ入力をより適切に処理するために、javax.vecmathのPoint3f / Point2fタプルを使用するように改善することもできます。
Alex Byrth 2017年

10

簡単な方法は次のとおりです。

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

代替案を説明する2つの優れたサイトは次のとおりです。

ブラックポーンウルフラム


3
ええと、その方法は正確には効率的ではなく、数値エラーが発生しやすい...
Kornel Kisielewicz

それは全く逆です、それは非常に非効率的です:-)それはただ一つの単純な方法ですが、それは簡単に実装できます。これにより発生する数値エラーの例を挙げていただけますか?
Simon P Stevens

私にとってこれは単にこのトピックのすべての答えの中で最高のようですが、三角形のエッジ上のポイントは三角形に含まれるように計算されていると思いますが、それを確実に制御することはできません。
Redu

正確に2piであるかどうかのチェックは、piの不合理を考えると、数値的に不可能です。ただし、角度の合計がpiよりも大きいかどうかを確認するだけです。
lonewarrior556

10

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 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>

につながる:

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

これは、Kornel Kisielewiczソリューション(呼び出し 25回、記憶1回、減算15回、乗算5回、比較5回)と非常によく似ており、時計回り/反時計回りの検出が必要な場合はさらに優れている可能性があります(呼び出し 6回、加算1回、減算2回の減算が必要です)。 、rghbで指摘されているように、解析解の行列式を使用した2つの乗算と1つの比較自体。


素晴らしい解決策。私はMSEにここに私の最後のアプローチとは全く同等であると思い:math.stackexchange.com/questions/51326/...
ジャックD'Aurizio

コードをそのままテストしましたが、機能しません(例p -4.69317198、-6.99191951 p0 -7.05846786 0.596718192 p1 -6.8703599 -2.36565161 p2 -4.69317198、-6.99191951)
Giovanni Funchal

@GiovanniFunchal奇妙な、あなたの例は、jsfiddle(最初の「ポイント」と「三角形」の定義を置き換える)と私のローカルPython実装の両方で私にとってうまくいきます。数値の精度の問題(小数点以下を削除してみてください)?
Cedric Dufour 2016

1
あなたが私のテストで最も速いようです:jsfiddle.net/eyal/gxw3632c/27。ただし、すべての方法の違いはごくわずかです。
Eyal、2017年

三角形(-1、-1)、(1、-1)、(0,1)と点(0、-1)を試してください。s(2)+ t(2)> d(2)であるため、trueを返す必要がある場合はfalseを返します。三角形のエッジの計算に問題があるようです。点pがp0とp1の境界にあり、<を<=に変換するなどの単純な問題ではないようです。
devnullicus

5

3つの面の法線を事前計算します。

  • サイドベクトルとフェース法線ベクトルの外積による3D。

  • 2Dでは、コンポーネントを交換して1つを否定するだけで、

次に、片側の内側/外側は、辺の法線と頂点からポイントへのベクトルのドット積で符号を変更する場合です。他の2つ(またはそれ以上)の側面について繰り返します。

利点:

  • たくさんのものが事前計算されているので、同じ三角形での多点テストに最適です。

  • 内側のポイントよりも外側の一般的なケースの早期拒否。(ポイント分布が片側に重み付けされている場合も、最初にその側をテストできます。)


5

以下は効率的な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))

そして出力例:

ここに画像の説明を入力してください


たとえば、三角形のポイント[(0,0)、(3,0)、(3,4)]、ポイント(1,1)または(0 、0)テスト陽性。時計回りと反時計回りの両方の三角形の点を試してみました。
ThorSummoner

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で、DX/DYは最も高いエッジの勾配です。この変換により、三角形のこちら側が垂直になります。そして、あなたはあなたが真ん中の水平線のどちら側にあるかを知っているので、三角形の片側に関してサインをテストすることで十分です。

ここに画像の説明を入力してください

勾配と、せん断された三角形の頂点と辺の方程式の係数をmとして事前計算したと仮定すると、最悪の場合X'X = m Y + p

  • 垂直分類のための2つの縦軸比較。
  • 必要に応じて、境界ボックスを拒否するための1つまたは2つの横座標比較。
  • の計算X' = X - m Y;
  • せん断された三角形の横座標との1つまたは2つの比較。
  • X >< m' Y + p'せん断された三角形の関連する辺に対する1つの符号テスト。

3

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


3

Pythonの他の関数、開発者の方法よりも高速で(少なくとも私にとっては)、CédricDufourソリューションに触発された:

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、それは偽ですが返されます
教皇教皇、

3

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/

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の内側にあります。

三角形の包含の例


私はこのコードに疲れてしまい、動作しません。常にFalseを返します。
xApple 2018

うーん...あなたはおそらく間違いを犯した。ここではその機能が動作しているフィドルです:jsfiddle.net/jniac/rctb3gfL
ジョセフMerdrignac

私はあなたのPython応答を見てきましたが、同じ方法を使用しています。もう1本の線(let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay))を使用する場合、これは三角形の巻き順を決定するためです。そのため、この方法はCWおよびCCWの三角形で機能します(jsFiddleを参照)。
ジョセフメルドリニャック

1
hm私は間違いを犯した、と私は書きました: let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)代わりに let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)、これは修正されました。報告をありがとう
ジョセフ・メルドリニャック

2

私はいくつかの単純なベクトル数学を使用して、アンドレアスが与えた重心座標の解を説明したいだけですが、それは理解しやすくなります。

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

ここに画像の説明を入力してください

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

ここに画像の説明を入力してください

  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となります。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]に属します。


側面がs = 0、t = 0、s + t = 1になるように、3つの頂点によって定義されたローカルフレームを使用していると言えます。アフィン座標変換は、線形代数のよく知られた操作です。
Yves Daoust

2

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

次の図を作成します。

point_in_triangle関数をテストする


1

隣接する2つの三角形の共通のエッジ上に点がある正確なエッジ条件があります。点は両方に存在することはできず、どちらの三角形にも存在することはできません。ポイントを割り当てるには、任意だが一貫した方法が必要です。たとえば、ポイントを通る水平線を描画します。線が三角形の反対側の右側と交差する場合、ポイントは三角形の内部にあるものとして扱われます。交差点が左側にある場合、ポイントは外側です。

ポイントのある線が水平の場合は、上/下を使用します。

ポイントが複数の三角形の共通の頂点上にある場合は、ポイントが最小の角度を形成する中心を持つ三角形を使用します。

もっと楽しく:3つのポイントは直線(0度)にできます(例:(0,0)-(0,10)-(0,5))。三角測量アルゴリズムでは、「耳」(0,10)を削除する必要があります。生成される「三角形」は、直線の縮退した場合です。


1

これは、ポイントが三角形の内部または外部にあるか、三角形の腕にあるかを決定する最も簡単な概念です。

点の決定は、行列式によって三角形の内側にあります。

点の決定は、行列式によって三角形の内側にあります

最も単純な作業コード:

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

最も簡単な方法で、すべてのタイプの三角形で機能するのは、単純に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


0

正直なところ、Simon P Stevenの答えと同じくらい簡単です。ですが、そのアプローチでは、三角形のエッジ上のポイントを含めるかどうかを確実に制御できません。

私のアプローチは少し異なりますが、非常に基本的です。次の三角形について考えてみましょう。

ここに画像の説明を入力してください

三角形のポイントを取得するには、3つの条件を満たす必要があります

  1. ACE角度(緑)はACB角度(赤)より小さくする必要があります
  2. ECB角度(青)はACB角度(赤)より小さくする必要があります
  3. | AB |の方程式にx値とy値を適用すると、ポイント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
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つの計算:l1、l2、l3はそれぞれ2つの乗算を必要とします。l1、l2、l3がわかると、結果はいくつかの基本的な比較とブール演算だけになります。


0

私が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(パフォーマンステストが含まれています)を見てください。別の関数で巻線チェックも行われています。または、下の「コードスニペットを実行」を押してください

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


-1
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;
} 

重心から変換されたほぼ完全なデカルト座標は、* v(x)および* w(y)のdouble内でエクスポートされます。いずれの場合も、両方のエクスポートdoubleの前に*文字が必要です。おそらく、* vおよび* wコードは、四角形の他の三角形にも使用できます。これにより、署名された時計回りのabcdクワッドから三角形abcのみが書き込まれました。

A---B
|..\\.o|  
|....\\.| 
D---C 

o点はABC三角形の内側にあり、2番目の三角形でこの関数をCDA方向に呼び出してテストします。四角形の後*v=1-*v;*w=1-*w;四角形の結果は正しいはずです。


-1

三角形が時計回りであることが確実である場合は、「制御可能な環境」で三角形のチェックインポイントが必要でした。そこで、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;
}

-3

三角形(x1、y1)、(x2、y2)、(x3、y3)の頂点によって形成される領域が正かどうかを確認する最も簡単な方法の1つ。

面積は次の式で計算できます。

1/2 [x1(y2–y3)+ x2(y3–y1)+ x3(y1–y2)]

またはpythonコードは次のように書くことができます:

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])]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.