web-dev-qa-db-fra.com

Comment déterminer si un point est dans un triangle 2D?

Existe-t-il un moyen facile de déterminer si un point se trouve dans un triangle? C'est 2D, pas 3D.

223
ET 0.618

En général, l'algorithme le plus simple (et tout à fait optimal) vérifie de quel côté du demi-plan créé par les arêtes se situe le point.

Voici quelques informations de haute qualité dans ce sujet sur GameDev , y compris des problèmes de performances.

Et voici un code pour vous aider à démarrer:

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);
}
236
Kornel Kisielewicz

Résoudre le système d'équation suivant:

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

Le point p est à l'intérieur du triangle si 0 <= s <= 1 et 0 <= t <= 1 et s + t <= 1.

s, t et 1 - s - t sont appelées les coordonnées barycentriques du point p.

151
Andreas Brinck

Je suis d'accord avec Andreas Brinck, les coordonnées barycentriques sont très pratiques pour cette tâche. Notez qu'il n'est pas nécessaire de résoudre un système d'équation à chaque fois: il suffit d'évaluer la solution analytique. En utilisant la notation Andreas ', la solution est la suivante:

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 est l'aire (signée) du triangle:

Area = 0.5 *(-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y);

Evaluez simplement s, t et 1-s-t. Le point p est à l'intérieur du triangle si et seulement s'ils sont tous positifs.

EDIT: Notez que l'expression ci-dessus pour la zone suppose que la numérotation des nœuds du triangle est dans le sens anti-horaire. Si la numérotation est dans le sens des aiguilles d'une montre, cette expression retournera une zone négative (mais avec une magnitude correcte). Le test lui-même (s>0 && t>0 && 1-s-t>0) ne dépend toutefois pas de la direction de la numérotation, car les expressions ci-dessus multipliées par 1/(2*Area) changent également de signe si l'orientation du nœud triangle change.

EDIT 2: Pour une efficacité de calcul encore meilleure, voir le commentaire de coproc ci-dessous (qui indique que si l’orientation des nœuds triangulaires (dans le sens des aiguilles d’une montre ou dans le sens contraire) est connue à l’avance, la division par 2*Area dans les expressions pour s et t peuvent être évitées). Voir aussi le code jsfiddle de Perro Azul dans les commentaires sous la réponse de Andreas Brinck.

101
andreasdr

J'ai écrit ce code avant une dernière tentative avec Google et la recherche de cette page, alors j'ai pensé le partager. C'est fondamentalement une version optimisée de la réponse de Kisielewicz. J'ai aussi examiné la méthode barycentrique, mais d'après l'article de Wikipédia, j'ai du mal à voir en quoi elle est plus efficace (je suppose qu'il existe une équivalence plus profonde). Quoi qu'il en soit, cet algorithme a l'avantage de ne pas utiliser la division; Un problème potentiel est le comportement de la détection Edge en fonction de l'orientation.

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

En mots, l’idée est la suivante: le point s est-il à gauche ou à droite des lignes AB et AC? Si c'est vrai, ça ne peut pas être à l'intérieur. Si faux, c'est au moins à l'intérieur des "cônes" qui satisfont à la condition. Maintenant que nous savons qu'un point à l'intérieur d'un trigon (triangle) doit se trouver du même côté de AB que BC (et aussi CA), nous vérifions s'ils sont différents. S'ils le font, ils ne peuvent pas être à l'intérieur, sinon ils doivent être à l'intérieur.

Certains mots-clés dans les calculs sont les demi-plans de ligne et le déterminant (produit croisé 2x2). Peut-être une façon plus pédagogique est-elle probablement de penser à cela comme un point situé si et seulement si c'est du même côté (gauche ou droite) de chacune des lignes AB, BC et CA La manière ci-dessus semblait un meilleur ajustement pour une optimisation cependant.

38
John Bananas

C # version de la méthode barycentrique publiée par andreasdr et Perro Azul. Notez que le calcul de la surface peut être évité si s et t ont des signes opposés. J'ai vérifié le comportement correct avec un test unitaire assez approfondi.

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

[ modifier ]
accepté suggestion de modification par @Pierre; voir les commentaires

25
Glenn Slayden

Un moyen simple est de:

trouver les vecteurs reliant le pointez sur chacun des trois triangles sommets et sommer les angles entre ces vecteurs. Si la somme de la angles est 2 * pi alors le point est à l'intérieur du triangle.

Deux bons sites expliquant les alternatives sont:

blackpawn et wolfram

10
Simon P Stevens

Version Java de la méthode barycentrique:

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

Le code ci-dessus fonctionnera correctement avec les entiers, en supposant qu'il n'y ait pas de débordement. Cela fonctionnera également avec les triangles horaire et antihoraire. Cela ne fonctionnera pas avec les triangles colinéaires (mais vous pouvez le vérifier en testant det == 0).

La version barycentrique est la plus rapide si vous souhaitez tester différents points avec le même triangle.

La version barycentrique n'est pas symétrique dans les 3 points triangulaires, elle est donc probablement moins cohérente que la version demi-plan Edge de Kornel Kisielewicz en raison d'erreurs d'arrondi en virgule flottante.

Crédit: J'ai créé le code ci-dessus à partir d'un article de Wikipedia sur les coordonnées barycentriques.

10
Adam Gawne-Cain

En utilisant la solution analytique aux coordonnées barycentriques (indiqué par Andreas Brinck) et:

  • ne pas distribuer la multiplication sur les termes entre parenthèses
  • éviter de calculer plusieurs fois les mêmes termes en les stockant
  • réduire les comparaisons (comme le soulignent coproc et Thomas Eding)

on peut minimiser le nombre d'opérations "coûteuses":

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

(Le code peut être collé dans Perro Azuljsfiddle )

Menant à:

  • variable "rappels": 30
  • stockage variable: 7
  • ajouts: 4
  • soustractions: 8
  • multiplications: 6
  • divisions: aucune
  • comparaisons: 4

Ceci se compare assez bien à la solution Kornel Kisielewicz (25 rappels, 1 stockage, 15 soustractions, 6 multiplications, 5 comparaisons), et pourrait même être meilleure si une détection sens horaire/anti-horaire est nécessaire (ce qui prend 6 rappels, 1 l'addition, 2 soustractions, 2 multiplications et 1 comparaison en elle-même, en utilisant le déterminant de solution analytique, comme indiqué par rhgb).

7
Cédric Dufour

Ce que je fais est de pré-calculer les normales à trois faces, 

  • en 3D par le produit croisé du vecteur latéral et du vecteur normal du visage.

  • en 2D en échangeant simplement des composants et en inversant un,

ensuite, à l'intérieur ou à l'extérieur d'un côté, lorsqu'un produit scalaire du côté normal et du vecteur sommet à point change de signe. Répétez l'opération pour les deux autres côtés (ou plus).

Avantages:

  • beaucoup de choses sont précalculées, ce qui est excellent pour les tests de points multiples sur le même triangle.

  • rejet précoce du cas courant de plus de points extérieurs qu'intérieurs. (également si la distribution de points pondérée sur un côté, peut tester ce côté en premier.)

5
psiman

Voici une implémentation efficace de 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))

et un exemple de sortie: 

enter image description here

4
Developer

Autre fonction dans python , plus rapide que Méthode du développeur (pour moi du moins) et inspirée par Cédric Dufour 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  )

Vous pouvez le tester avec:

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

Il en faut beaucoup pour tracer, mais cette grille est testée en 0.0195319652557 secondes contre 0.0844349861145 secondes de Code du développeur .

Enfin le commentaire de code:

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

Si vous connaissez les coordonnées des trois sommets et les coordonnées du point spécifique, vous pouvez obtenir l'aire du triangle complet. Ensuite, calculez l'aire des trois segments du triangle (un point étant le point donné et les deux autres étant les deux sommets du triangle) Ainsi, vous obtiendrez l'aire des trois segments de triangle. Si la somme de ces zones est égale à la surface totale (que vous avez obtenue précédemment), le point doit être à l'intérieur du triangle. Sinon, le point n'est pas à l'intérieur du triangle. Cela devrait marcher. S'il y a des problèmes, faites le moi savoir. Je vous remercie.

3
ihayet

Si vous recherchez de la rapidité, voici une procédure qui pourrait vous aider.

Triez les sommets des triangles sur leurs ordonnées. Cela prend au pire trois comparaisons. Soit Y0, Y1, Y2 les trois valeurs triées. En traçant trois horizontales à travers elles, vous divisez le plan en deux demi-plans et deux dalles. Soit Y l'ordonnée du point de requête.

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

Coûte deux autres comparaisons. Comme vous le voyez, un rejet rapide est obtenu pour les points situés en dehors de la "dalle de liaison".

Vous pouvez éventuellement fournir un test sur les abscisses pour un rejet rapide à gauche et à droite (X <= X0' or X >= X2'). Cela implémentera un test rapide du cadre de sélection en même temps, mais vous devrez également trier les abscisses.

Finalement, vous devrez calculer le signe du point donné par rapport aux deux côtés du triangle qui délimitent la dalle correspondante (supérieure ou inférieure). Le test a la forme:

((X - Xi) * (Y - Yj) > (X - Xi) * (Y - Yj)) == ((X - Xi) * (Y - Yk) > (X - Xi) * (Y - Yk))

La discussion complète sur les combinaisons i, j, k (il y en a six, en fonction du résultat du tri) sort du champ de cette réponse et est "laissée comme un exercice au lecteur"; pour plus d'efficacité, ils doivent être codés en dur.

Si vous pensez que cette solution est complexe, observez qu’elle implique principalement des comparaisons simples (dont certaines peuvent être précalculées), plus 6 soustractions et 4 multiplications en cas d’échec du test du cadre de sélection. Ce dernier coût est difficile à battre car dans le pire des cas, vous ne pouvez pas éviter de comparer le point de test à deux côtés (aucune méthode n’a un coût inférieur, certaines aggravent le problème, comme 15 soustractions et 6 multiplications, parfois des divisions).

UPDATE: Plus rapide avec une transformation en cisaillement

Comme expliqué ci-dessus, vous pouvez rapidement localiser le point à l'intérieur de l'une des quatre bandes horizontales délimitées par les trois ordonnées du sommet, à l'aide de deux comparaisons.

Vous pouvez éventuellement effectuer un ou deux tests X supplémentaires pour vérifier l'insiduité dans le cadre de sélection (lignes en pointillés).

Ensuite, considérons la transformation "cisaillement" donnée par X'= X - m Y, Y' = Y, où m est la pente DX/DY pour le bord le plus élevé. Cette transformation rendra ce côté du triangle vertical. Et puisque vous savez de quel côté de l’horizontale moyenne vous vous trouvez, il suffit de tester le signe en ce qui concerne un seul côté du triangle.

enter image description here

En supposant que vous avez précalculé la pente m, ainsi que le X' pour les sommets des triangles cisaillés et les coefficients des équations des côtés comme X = m Y + p, vous aurez besoin dans le pire des cas.

  • deux comparaisons d'ordonnées pour la classification verticale;
  • éventuellement une ou deux comparaisons en abscisse pour le rejet de la boîte englobante;
  • calcul de X' = X - m Y;
  • une ou deux comparaisons avec les abscisses du triangle cisaillé;
  • un signe test X >< m' Y + p' contre le côté concerné du triangle cisaillé.
3
Yves Daoust

Il existe des conditions de bord difficiles où un point est exactement sur le bord commun de deux triangles adjacents. Le point ne peut pas être dans les deux, ni dans aucun des triangles. Vous avez besoin d’une manière arbitraire mais cohérente d’attribuer le point. Par exemple, tracez une ligne horizontale à travers le point. Si la ligne croise l'autre côté du triangle à droite, le point est traité comme s'il se trouvait à l'intérieur du triangle. Si l'intersection est à gauche, le point est à l'extérieur.

Si la ligne sur laquelle le point est situé est horizontale, utilisez ci-dessus/ci-dessous.

Si le point se trouve sur le sommet commun de plusieurs triangles, utilisez le triangle dont le centre forme le plus petit angle.

Plus amusant: trois points peuvent être en ligne droite (zéro degré), par exemple (0,0) - (0,10) - (0,5). Dans un algorithme de triangulation, "l'oreille" (0,10) doit être tronquée, le "triangle" généré étant le cas dégénéré d'une ligne droite.

1
Pierre

Voici une solution en python efficace, documentée et contenant trois unittests. C'est une qualité professionnelle et prête à être intégrée dans votre projet sous la forme d'un module tel quel.

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)

Il existe un test graphique facultatif supplémentaire pour l'algorithme ci-dessus pour confirmer sa validité:

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

Produire le graphique suivant:

 Test the point_in_triangle function

1
xApple

Je veux juste utiliser un simple calcul vectoriel pour expliquer la solution de coordonnées barycentriques donnée par Andreas, ce sera beaucoup plus facile à comprendre.

  1. La zone A est définie comme tout vecteur donné par s * v02 + t * v01, avec la condition s> = 0 et t> = 0. Si un point quelconque du triangle v0, v1, v2 doit être situé dans la zone A.

 enter image description here

  1. Si davantage restreint s, et t appartient à [0, 1]. On obtient la zone B qui contient tous les vecteurs de s * v02 + t * v01, avec la condition s, t appartient à [0, 1]. Il convient de noter que la partie basse de la zone B est le miroir de Triangle v0, v1, v2. Le problème vient si nous pouvons donner certaines conditions de s, et t, pour exclure encore la partie basse de la zone B.

 enter image description here

  1. Supposons que nous donnions une valeur s et que t change dans [0, 1]. Dans l'image suivante, le point p est sur le bord de v1v2. Tous les vecteurs de s * v02 + t * v01 situés le long de la ligne pointillée par simple somme vectorielle. À v1v2 et au point de croisement de la ligne tiret p, nous avons: 

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

on obtient 1 - s = tp, puis 1 = s + tp. Si un t> tp, qui 1 <s + t où est sur la ligne à double tiret, le vecteur est en dehors du triangle, tout t <= tp, qui 1> = s + t où est sur une ligne à tiret unique, le vecteur est à l'intérieur du triangle. 

Alors, si nous donnons un s quelconque dans [0, 1], le t correspondant doit correspondre à 1> = s + t, pour le vecteur triangle intérieur. 

 enter image description here

Donc finalement nous obtenons v = s * v02 + t * v01, v est le triangle intérieur avec la condition s, t, s + t appartient à [0, 1]. Puis traduisons au point, nous avons

p - p0 = s * (p1 - p0) + t * (p2 - p0), avec s, t, s + t dans [0, 1]

qui est identique à la solution d'Andreas pour résoudre le système d'équation p = p0 + s * (p1 - p0) + t * (p2 - p0), avec s, t, s + t appartiennent à [0, 1] .

1
Orup

J'avais besoin de vérifier le triangle dans "environnement contrôlable" lorsque vous êtes absolument certain que les triangles seront dans le sens des aiguilles d'une montre. J'ai donc pris le texte de Perro Azul et je l'ai modifié comme suggéré par coproc pour de tels cas; a également supprimé les multiplications redondantes 0,5 et 2 car elles s'annulent simplement.

http://jsfiddle.net/dog_funtom/H7D7g/

Voici le code C # équivalent pour Unity:

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

Honnêtement, c'est aussi simple que La réponse de Simon P. Steven Cependant, avec cette approche, vous ne pouvez pas vraiment contrôler si vous souhaitez ou non inclure les points sur les bords du triangle.

Mon approche est un peu différente mais très basique. Considérons le triangle suivant;

 enter image description here

Pour avoir le point dans le triangle, nous devons remplir 3 conditions

  1. L'angle ACE (vert) doit être inférieur à l'angle ACB (rouge)
  2. L'angle de la BCE (bleu) doit être inférieur à l'angle de la BCA (rouge)
  3. Le point E et le point C devraient avoir le même signe lorsque leurs valeurs x et y sont appliquées à l'équation de | AB | ligne.

Dans cette méthode, vous avez le contrôle total pour inclure ou exclure le point individuellement. Donc, vous pouvez vérifier si un point est dans le triangle incluant uniquement le | AC | Edge par exemple.

Donc, ma solution en JavaScript serait la suivante:

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

La façon la plus simple et qui fonctionne avec tous les types de triangles est simplement de déterminer les angles des angles des points P, A, B et C. Si l'un des angles est supérieur à 180,0 degrés, il est extérieur, si 180,0 il est situé sur la circonférence et s'il est triché contre vous et inférieur à 180,0, il est situé à l'intérieur. Jetez un coup d'œil pour comprendre http: // maths -physics-psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html

0
Bela Bessenyei

Puisqu'il n'y a pas de réponse JS,
Solution horaire et antihoraire:

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: il y avait une faute de frappe pour le calcul det (cy - ay au lieu de cx - ax), ceci est corrigé.

https://jsfiddle.net/jniac/rctb3gfL/ enter image description here 

J'utilise ici la même méthode que celle décrite ci-dessus: un point est dans ABC s'il est respectivement du "même" côté de chaque ligne AB, BC, CA.  triangle inclusion example 

0
Joseph Merdrignac
        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

Code supposément performant que j'ai adapté en JavaScript (article ci-dessous):

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) - pour les triangles dans le sens anti-horaire

pointInTriangle (p, p0, p1, p2) - pour les triangles dans le sens des aiguilles d'une montre

Regardez dans jsFiddle (test de performance inclus), il y a aussi une vérification distincte dans une fonction distincte http://jsfiddle.net/z7x0udf7/3/

Inspiré par ceci: 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);
}

Ça ne peut pas être plus efficace que ça! Chaque côté d’un triangle peut avoir une position et une orientation indépendantes, d’où trois calculs: l1, l2 et l3 sont absolument nécessaires et impliquent 2 multiplications chacun. Une fois que l1, l2 et l3 sont connus, il ne reste que quelques comparaisons de base et opérations booléennes.

0
Ajay Anand