[algorithm] How to determine if a point is in a 2D triangle?

Is there an easy way to determine if a point is inside a triangle? It's 2D, not 3D.

This question is related to algorithm math geometry

The answer is


Honestly it is as simple as Simon P Steven's answer however with that approach you don't have a solid control on whether you want the points on the edges of the triangle to be included or not.

My approach is a little different but very basic. Consider the following triangle;

enter image description here

In order to have the point in the triangle we have to satisfy 3 conditions

  1. ACE angle (green) should be smaller than ACB angle (red)
  2. ECB angle (blue) should be smaller than ACB angle (red)
  3. Point E and Point C shoud have the same sign when their x and y values are applied to the equation of the |AB| line.

In this method you have full control to include or exclude the point on the edges individually. So you may check if a point is in the triangle including only the |AC| edge for instance.

So my solution in JavaScript would be as follows;

_x000D_
_x000D_
function isInTriangle(t,p){_x000D_
_x000D_
  function isInBorder(a,b,c,p){_x000D_
    var m = (a.y - b.y) / (a.x - b.x);                     // calculate the slope_x000D_
    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);_x000D_
  }_x000D_
  _x000D_
  function findAngle(a,b,c){                               // calculate the C angle from 3 points._x000D_
    var ca = Math.hypot(c.x-a.x, c.y-a.y),                 // ca edge length_x000D_
        cb = Math.hypot(c.x-b.x, c.y-b.y),                 // cb edge length_x000D_
        ab = Math.hypot(a.x-b.x, a.y-b.y);                 // ab edge length_x000D_
    return Math.acos((ca*ca + cb*cb - ab*ab) / (2*ca*cb)); // return the C angle_x000D_
  }_x000D_
_x000D_
  var pas = t.slice(1)_x000D_
             .map(tp => findAngle(p,tp,t[0])),             // find the angle between (p,t[0]) with (t[1],t[0]) & (t[2],t[0])_x000D_
       ta = findAngle(t[1],t[2],t[0]);_x000D_
  return pas[0] < ta && pas[1] < ta && isInBorder(t[1],t[2],t[0],p);_x000D_
}_x000D_
_x000D_
var triangle = [{x:3, y:4},{x:10, y:8},{x:6, y:10}],_x000D_
      point1 = {x:3, y:9},_x000D_
      point2 = {x:7, y:9};_x000D_
_x000D_
console.log(isInTriangle(triangle,point1));_x000D_
console.log(isInTriangle(triangle,point2));
_x000D_
_x000D_
_x000D_


Other function in python, faster than Developer's method (for me at least) and inspired by 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  )

You can test it with:

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

It takes a lot to plot, but that grid is tested in 0.0195319652557 seconds against 0.0844349861145 seconds of Developer's code.

Finally the code comment:

# 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

Here is an efficient Python implementation:

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

and an example output:

enter image description here


By using the analytic solution to the barycentric coordinates (pointed out by Andreas Brinck) and:

  • not distributing the multiplication over the parenthesized terms
  • avoiding computing several times the same terms by storing them
  • reducing comparisons (as pointed out by coproc and Thomas Eding)

One can minimize the number of "costly" operations:

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

Code can be pasted in Perro Azul jsfiddle or try it by clicking "Run code snippet" below

_x000D_
_x000D_
var ctx = $("canvas")[0].getContext("2d");_x000D_
var W = 500;_x000D_
var H = 500;_x000D_
_x000D_
var point = { x: W / 2, y: H / 2 };_x000D_
var triangle = randomTriangle();_x000D_
_x000D_
$("canvas").click(function(evt) {_x000D_
    point.x = evt.pageX - $(this).offset().left;_x000D_
    point.y = evt.pageY - $(this).offset().top;_x000D_
    test();_x000D_
});_x000D_
_x000D_
$("canvas").dblclick(function(evt) {_x000D_
    triangle = randomTriangle();_x000D_
    test();_x000D_
});_x000D_
_x000D_
test();_x000D_
_x000D_
function test() {_x000D_
    var result = ptInTriangle(point, triangle.a, triangle.b, triangle.c);_x000D_
    _x000D_
    var info = "point = (" + point.x + "," + point.y + ")\n";_x000D_
    info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";_x000D_
    info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";_x000D_
    info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";_x000D_
    info += "result = " + (result ? "true" : "false");_x000D_
_x000D_
    $("#result").text(info);_x000D_
    render();_x000D_
}_x000D_
_x000D_
function ptInTriangle(p, p0, p1, p2) {_x000D_
    var A = 1/2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);_x000D_
    var sign = A < 0 ? -1 : 1;_x000D_
    var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign;_x000D_
    var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign;_x000D_
    _x000D_
    return s > 0 && t > 0 && (s + t) < 2 * A * sign;_x000D_
}_x000D_
_x000D_
function render() {_x000D_
    ctx.fillStyle = "#CCC";_x000D_
    ctx.fillRect(0, 0, 500, 500);_x000D_
    drawTriangle(triangle.a, triangle.b, triangle.c);_x000D_
    drawPoint(point);_x000D_
}_x000D_
_x000D_
function drawTriangle(p0, p1, p2) {_x000D_
    ctx.fillStyle = "#999";_x000D_
    ctx.beginPath();_x000D_
    ctx.moveTo(p0.x, p0.y);_x000D_
    ctx.lineTo(p1.x, p1.y);_x000D_
    ctx.lineTo(p2.x, p2.y);_x000D_
    ctx.closePath();_x000D_
    ctx.fill();_x000D_
    ctx.fillStyle = "#000";_x000D_
    ctx.font = "12px monospace";_x000D_
    ctx.fillText("1", p0.x, p0.y);_x000D_
    ctx.fillText("2", p1.x, p1.y);_x000D_
    ctx.fillText("3", p2.x, p2.y);_x000D_
}_x000D_
_x000D_
function drawPoint(p) {_x000D_
    ctx.fillStyle = "#F00";_x000D_
    ctx.beginPath();_x000D_
    ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);_x000D_
    ctx.fill();_x000D_
}_x000D_
_x000D_
function rand(min, max) {_x000D_
 return Math.floor(Math.random() * (max - min + 1)) + min;_x000D_
}_x000D_
_x000D_
function randomTriangle() {_x000D_
    return {_x000D_
        a: { x: rand(0, W), y: rand(0, H) },_x000D_
        b: { x: rand(0, W), y: rand(0, H) },_x000D_
        c: { x: rand(0, W), y: rand(0, H) }_x000D_
    };_x000D_
}
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>_x000D_
<pre>Click: place the point._x000D_
Double click: random triangle.</pre>_x000D_
<pre id="result"></pre>_x000D_
<canvas width="500" height="500"></canvas>
_x000D_
_x000D_
_x000D_

Leading to:

  • variable "recalls": 30
  • variable storage: 7
  • additions: 4
  • subtractions: 8
  • multiplications: 6
  • divisions: none
  • comparisons: 4

This compares quite well with Kornel Kisielewicz solution (25 recalls, 1 storage, 15 subtractions, 6 multiplications, 5 comparisons), and might be even better if clockwise/counter-clockwise detection is needed (which takes 6 recalls, 1 addition, 2 subtractions, 2 multiplications and 1 comparison in itself, using the analytic solution determinant, as pointed out by rhgb).


One of the easiest ways to check if the area formed by the vertices of triangle (x1,y1),(x2,y2),(x3,y3) is positive 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])]

This is the simplest concept to determine if a point is inside or outside the triangle or on an arm of a triangle.

Determination of a point is inside a triangle by determinants:

Determination of a point is inside a triangle by determinants

The simplest working code:

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

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

almost perfect Cartesian coordinates converted from barycentric are exported within *v (x) and *w (y) doubles. Both export doubles should have a * char before in every case, likely: *v and *w Code can be used for the other triangle of a quadrangle too. Hereby signed wrote only triangle abc from the clockwise abcd quad.

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

the o point is inside ABC triangle for testing with with second triangle call this function CDA direction, and results should be correct after *v=1-*v; and *w=1-*w; for the quadrangle


If you know the co-ordinates of the three vertices and the co-ordinates of the specific point, then you can get the area of the complete triangle. Afterwards, calculate the area of the three triangle segments (one point being the point given and the other two being any two vertices of the triangle). Thus, you will get the area of the three triangle segments. If the sum of these areas are equal to the total area (that you got previously), then, the point should be inside the triangle. Otherwise, the point is not inside the triangle. This should work. If there are any issues, let me know. Thank you.


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

It can not be more efficient than this! Each side of a triangle can have independent position and orientation, hence three calculations: l1, l2 and l3 are definitely needed involving 2 multiplications each. Once l1, l2 and l3 are known, result is just a few basic comparisons and boolean operations away.


I just want to use some simple vector math to explain the barycentric coordinates solution which Andreas had given, it will be way easier to understand.

  1. Area A is defined as any vector given by s * v02 + t * v01, with condition s >= 0 and t >= 0. If any point inside the triangle v0, v1, v2, it must be inside Area A.

enter image description here

  1. If further restrict s, and t belongs to [0, 1]. We get Area B which contains all vectors of s * v02 + t * v01, with condition s, t belongs to [0, 1]. It is worth to note that the low part of the Area B is the mirror of Triangle v0, v1, v2. The problem comes to if we can give certain condition of s, and t, to further excluding the low part of Area B.

enter image description here

  1. Assume we give a value s, and t is changing in [0, 1]. In the following pic, point p is on the edge of v1v2. All the vectors of s * v02 + t * v01 which are along the dash line by simple vector sum. At v1v2 and dash line cross point p, we have:

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

we get 1 - s = tp, then 1 = s + tp. If any t > tp, which 1 < s + t where is on the double dash line, the vector is outside the triangle, any t <= tp, which 1 >= s + t where is on single dash line, the vector is inside the triangle.

Then if we given any s in [0, 1], the corresponding t must meet 1 >= s + t, for the vector inside triangle.

enter image description here

So finally we get v = s * v02 + t * v01, v is inside triangle with condition s, t, s+t belongs to [0, 1]. Then translate to point, we have

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

which is the same as Andreas' solution to solve equation system p = p0 + s * (p1 - p0) + t * (p2 - p0), with s, t, s + t belong to [0, 1].


What I do is precalculate the three face normals,

  • in 3D by cross product of side vector and the face normal vector.

  • in 2D by simply swapping components and negating one,

then inside/outside for any one side is when a dot product of the side normal and the vertex to point vector, change sign. Repeat for other two (or more) sides.

Benefits:

  • a lot is precalculated so great for multiple point testing on same triangle.

  • early rejection of common case of more outside than inside points. (also if point distribution weighted to one side, can test that side first.)


Solve the following equation system:

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

The point p is inside the triangle if 0 <= s <= 1 and 0 <= t <= 1 and s + t <= 1.

s,t and 1 - s - t are called the barycentric coordinates of the point p.


Since there's no JS answer,
Clockwise & Counter-Clockwise solution:

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: there was a typo for det computation (cy - ay instead of cx - ax), this is fixed.

https://jsfiddle.net/jniac/rctb3gfL/

_x000D_
_x000D_
function triangleContains(ax, ay, bx, by, cx, cy, x, y) {_x000D_
_x000D_
    let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)_x000D_
 _x000D_
    return  det * ((bx - ax) * (y - ay) - (by - ay) * (x - ax)) > 0 &&_x000D_
            det * ((cx - bx) * (y - by) - (cy - by) * (x - bx)) > 0 &&_x000D_
            det * ((ax - cx) * (y - cy) - (ay - cy) * (x - cx)) > 0 _x000D_
_x000D_
}_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
let width = 500, height = 500_x000D_
_x000D_
// clockwise_x000D_
let triangle1 = {_x000D_
_x000D_
 A : { x: 10, y: -10 },_x000D_
 C : { x: 20, y: 100 },_x000D_
 B : { x: -90, y: 10 },_x000D_
 _x000D_
 color: '#f00',_x000D_
_x000D_
}_x000D_
_x000D_
// counter clockwise_x000D_
let triangle2 = {_x000D_
_x000D_
 A : { x: 20, y: -60 },_x000D_
 B : { x: 90, y: 20 },_x000D_
 C : { x: 20, y: 60 },_x000D_
_x000D_
 color: '#00f',_x000D_
 _x000D_
}_x000D_
_x000D_
_x000D_
let scale = 2_x000D_
let mouse = { x: 0, y: 0 }_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
// DRAW >_x000D_
_x000D_
let wrapper = document.querySelector('div.wrapper')_x000D_
_x000D_
wrapper.onmousemove = ({ layerX:x, layerY:y }) => {_x000D_
 _x000D_
 x -= width / 2_x000D_
 y -= height / 2_x000D_
 x /= scale_x000D_
 y /= scale_x000D_
 _x000D_
 mouse.x = x_x000D_
 mouse.y = y_x000D_
 _x000D_
 drawInteractive()_x000D_
_x000D_
}_x000D_
_x000D_
function drawArrow(ctx, A, B) {_x000D_
_x000D_
 let v = normalize(sub(B, A), 3)_x000D_
 let I = center(A, B)_x000D_
 _x000D_
 let p_x000D_
 _x000D_
 p = add(I, rotate(v, 90), v)_x000D_
 ctx.moveTo(p.x, p.y)_x000D_
 ctx.lineTo(I.x, I .y)_x000D_
 p = add(I, rotate(v, -90), v)_x000D_
 ctx.lineTo(p.x, p.y)_x000D_
_x000D_
}_x000D_
_x000D_
function drawTriangle(ctx, { A, B, C, color }) {_x000D_
_x000D_
 ctx.beginPath()_x000D_
 ctx.moveTo(A.x, A.y)_x000D_
 ctx.lineTo(B.x, B.y)_x000D_
 ctx.lineTo(C.x, C.y)_x000D_
 ctx.closePath()_x000D_
 _x000D_
 ctx.fillStyle = color + '6'_x000D_
 ctx.strokeStyle = color_x000D_
 ctx.fill()_x000D_
 _x000D_
 drawArrow(ctx, A, B)_x000D_
 drawArrow(ctx, B, C)_x000D_
 drawArrow(ctx, C, A)_x000D_
 _x000D_
 ctx.stroke()_x000D_
_x000D_
}_x000D_
_x000D_
function contains({ A, B, C }, P) {_x000D_
_x000D_
 return triangleContains(A.x, A.y, B.x, B.y, C.x, C.y, P.x, P.y)_x000D_
_x000D_
}_x000D_
_x000D_
function resetCanvas(canvas) {_x000D_
_x000D_
 canvas.width = width_x000D_
 canvas.height = height_x000D_
 _x000D_
 let ctx = canvas.getContext('2d')_x000D_
_x000D_
 ctx.resetTransform()_x000D_
 ctx.clearRect(0, 0, width, height)_x000D_
 ctx.setTransform(scale, 0, 0, scale, width/2, height/2)_x000D_
 _x000D_
}_x000D_
_x000D_
function drawDots() {_x000D_
_x000D_
 let canvas = document.querySelector('canvas#dots')_x000D_
 let ctx = canvas.getContext('2d')_x000D_
_x000D_
 resetCanvas(canvas)_x000D_
 _x000D_
 let count = 1000_x000D_
_x000D_
 for (let i = 0; i < count; i++) {_x000D_
_x000D_
  let x = width * (Math.random() - .5)_x000D_
  let y = width * (Math.random() - .5)_x000D_
  _x000D_
  ctx.beginPath()_x000D_
  ctx.ellipse(x, y, 1, 1, 0, 0, 2 * Math.PI)_x000D_
  _x000D_
  if (contains(triangle1, { x, y })) {_x000D_
  _x000D_
   ctx.fillStyle = '#f00'_x000D_
  _x000D_
  } else if (contains(triangle2, { x, y })) {_x000D_
  _x000D_
   ctx.fillStyle = '#00f'_x000D_
  _x000D_
  } else {_x000D_
  _x000D_
   ctx.fillStyle = '#0003'_x000D_
  _x000D_
  }_x000D_
_x000D_
  _x000D_
  ctx.fill()_x000D_
  _x000D_
 }_x000D_
 _x000D_
}_x000D_
_x000D_
function drawInteractive() {_x000D_
_x000D_
 let canvas = document.querySelector('canvas#interactive')_x000D_
 let ctx = canvas.getContext('2d')_x000D_
_x000D_
 resetCanvas(canvas)_x000D_
 _x000D_
 ctx.beginPath()_x000D_
 ctx.moveTo(0, -height/2)_x000D_
 ctx.lineTo(0, height/2)_x000D_
 ctx.moveTo(-width/2, 0)_x000D_
 ctx.lineTo(width/2, 0)_x000D_
 ctx.strokeStyle = '#0003'_x000D_
 ctx.stroke()_x000D_
 _x000D_
 drawTriangle(ctx, triangle1)_x000D_
 drawTriangle(ctx, triangle2)_x000D_
 _x000D_
 ctx.beginPath()_x000D_
 ctx.ellipse(mouse.x, mouse.y, 4, 4, 0, 0, 2 * Math.PI)_x000D_
 _x000D_
 if (contains(triangle1, mouse)) {_x000D_
 _x000D_
  ctx.fillStyle = triangle1.color + 'a'_x000D_
  ctx.fill()_x000D_
  _x000D_
 } else if (contains(triangle2, mouse)) {_x000D_
 _x000D_
  ctx.fillStyle = triangle2.color + 'a'_x000D_
  ctx.fill()_x000D_
  _x000D_
 } else {_x000D_
 _x000D_
  ctx.strokeStyle = 'black'_x000D_
  ctx.stroke()_x000D_
  _x000D_
 }_x000D_
 _x000D_
}_x000D_
_x000D_
drawDots()_x000D_
drawInteractive()_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
// trigo_x000D_
_x000D_
function add(...points) {_x000D_
 _x000D_
 let x = 0, y = 0_x000D_
 _x000D_
 for (let point of points) {_x000D_
 _x000D_
  x += point.x_x000D_
  y += point.y_x000D_
 _x000D_
 }_x000D_
 _x000D_
 return { x, y }_x000D_
_x000D_
}_x000D_
_x000D_
function center(...points) {_x000D_
 _x000D_
 let x = 0, y = 0_x000D_
 _x000D_
 for (let point of points) {_x000D_
 _x000D_
  x += point.x_x000D_
  y += point.y_x000D_
 _x000D_
 }_x000D_
 _x000D_
 x /= points.length_x000D_
 y /= points.length_x000D_
 _x000D_
 return { x, y }_x000D_
_x000D_
}_x000D_
_x000D_
function sub(A, B) {_x000D_
_x000D_
 let x = A.x - B.x_x000D_
 let y = A.y - B.y_x000D_
 _x000D_
 return { x, y }_x000D_
_x000D_
}_x000D_
_x000D_
function normalize({ x, y }, length = 10) {_x000D_
_x000D_
 let r = length / Math.sqrt(x * x + y * y)_x000D_
 _x000D_
 x *= r_x000D_
 y *= r_x000D_
 _x000D_
 return { x, y }_x000D_
_x000D_
}_x000D_
_x000D_
function rotate({ x, y }, angle = 90) {_x000D_
_x000D_
 let length = Math.sqrt(x * x + y * y)_x000D_
 _x000D_
 angle *= Math.PI / 180_x000D_
 angle += Math.atan2(y, x)_x000D_
 _x000D_
 x = length * Math.cos(angle)_x000D_
 y = length * Math.sin(angle)_x000D_
 _x000D_
 return { x, y }_x000D_
_x000D_
}
_x000D_
* {_x000D_
 margin: 0;_x000D_
}_x000D_
_x000D_
html {_x000D_
 font-family: monospace;_x000D_
}_x000D_
_x000D_
body {_x000D_
 padding: 32px;_x000D_
}_x000D_
_x000D_
span.red {_x000D_
 color: #f00;_x000D_
}_x000D_
_x000D_
span.blue {_x000D_
 color: #00f;_x000D_
}_x000D_
_x000D_
canvas {_x000D_
 position: absolute;_x000D_
 border: solid 1px #ddd;_x000D_
}
_x000D_
<p><span class="red">red triangle</span> is clockwise</p>_x000D_
<p><span class="blue">blue triangle</span> is couter clockwise</p>_x000D_
<br>_x000D_
<div class="wrapper">_x000D_
 <canvas id="dots"></canvas>_x000D_
 <canvas id="interactive"></canvas>_x000D_
</div>
_x000D_
_x000D_
_x000D_

enter image description here

I'm using here the same method as described above: a point is inside ABC if it is respectively on the "same" side of each line AB, BC, CA.

triangle inclusion example


The easiest way and it works with all types of triangles is simply determine the angles of the P point A, B , C points angles. If any of the angles are bigger than 180.0 degree then it is outside, if 180.0 then it is on the circumference and if acos cheating on you and less than 180.0 then it is inside.Take a look for understanding http://math-physics-psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html


Supposedly high-performance code which I adapted in JavaScript (article below):

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) - for counter-clockwise triangles
  • pointInTriangle(p, p0, p1, p2) - for clockwise triangles

Look in jsFiddle (performance test included), there's also winding checking in a separate function. Or press "Run code snippet" below

_x000D_
_x000D_
var ctx = $("canvas")[0].getContext("2d");_x000D_
var W = 500;_x000D_
var H = 500;_x000D_
_x000D_
var point = { x: W / 2, y: H / 2 };_x000D_
var triangle = randomTriangle();_x000D_
_x000D_
$("canvas").click(function(evt) {_x000D_
    point.x = evt.pageX - $(this).offset().left;_x000D_
    point.y = evt.pageY - $(this).offset().top;_x000D_
    test();_x000D_
});_x000D_
_x000D_
$("canvas").dblclick(function(evt) {_x000D_
    triangle = randomTriangle();_x000D_
    test();_x000D_
});_x000D_
_x000D_
document.querySelector('#performance').addEventListener('click', _testPerformance);_x000D_
_x000D_
test();_x000D_
_x000D_
function test() {_x000D_
    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);_x000D_
    _x000D_
    var info = "point = (" + point.x + "," + point.y + ")\n";_x000D_
    info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";_x000D_
    info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";_x000D_
    info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";_x000D_
    info += "result = " + (result ? "true" : "false");_x000D_
_x000D_
    $("#result").text(info);_x000D_
    render();_x000D_
}_x000D_
_x000D_
function _testPerformance () {_x000D_
 var px = [], py = [], p0x = [], p0y = [], p1x = [], p1y = [], p2x = [], p2y = [], p = [], p0 = [], p1 = [], p2 = [];_x000D_
    _x000D_
 for(var i = 0; i < 1000000; i++) {_x000D_
    p[i] = {x: Math.random() * 100, y: Math.random() * 100};_x000D_
    p0[i] = {x: Math.random() * 100, y: Math.random() * 100};_x000D_
    p1[i] = {x: Math.random() * 100, y: Math.random() * 100};_x000D_
    p2[i] = {x: Math.random() * 100, y: Math.random() * 100};_x000D_
  }_x000D_
  console.time('optimal: pointInTriangle');_x000D_
  for(var i = 0; i < 1000000; i++) {_x000D_
    pointInTriangle(p[i], p0[i], p1[i], p2[i]);_x000D_
  }_x000D_
  console.timeEnd('optimal: pointInTriangle');_x000D_
_x000D_
  console.time('original: ptInTriangle');_x000D_
  for(var i = 0; i < 1000000; i++) {_x000D_
   ptInTriangle(p[i], p0[i], p1[i], p2[i]);_x000D_
  }_x000D_
  console.timeEnd('original: ptInTriangle');_x000D_
}_x000D_
_x000D_
function pointInTriangle (p, p0, p1, p2) {_x000D_
 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;_x000D_
}_x000D_
_x000D_
function ptInTriangle(p, p0, p1, p2) {_x000D_
    var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);_x000D_
    var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);_x000D_
_x000D_
    if (s <= 0 || t <= 0) return false;_x000D_
_x000D_
    var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);_x000D_
    return (s + t) < A;_x000D_
}_x000D_
_x000D_
function render() {_x000D_
    ctx.fillStyle = "#CCC";_x000D_
    ctx.fillRect(0, 0, 500, 500);_x000D_
    drawTriangle(triangle.a, triangle.b, triangle.c);_x000D_
    drawPoint(point);_x000D_
}_x000D_
_x000D_
function checkClockwise(p0, p1, p2) {_x000D_
    var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);_x000D_
    return A > 0;_x000D_
}_x000D_
_x000D_
function drawTriangle(p0, p1, p2) {_x000D_
    ctx.fillStyle = "#999";_x000D_
    ctx.beginPath();_x000D_
    ctx.moveTo(p0.x, p0.y);_x000D_
    ctx.lineTo(p1.x, p1.y);_x000D_
    ctx.lineTo(p2.x, p2.y);_x000D_
    ctx.closePath();_x000D_
    ctx.fill();_x000D_
    ctx.fillStyle = "#000";_x000D_
    ctx.font = "12px monospace";_x000D_
    ctx.fillText("1", p0.x, p0.y);_x000D_
    ctx.fillText("2", p1.x, p1.y);_x000D_
    ctx.fillText("3", p2.x, p2.y);_x000D_
}_x000D_
_x000D_
function drawPoint(p) {_x000D_
    ctx.fillStyle = "#F00";_x000D_
    ctx.beginPath();_x000D_
    ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);_x000D_
    ctx.fill();_x000D_
}_x000D_
_x000D_
function rand(min, max) {_x000D_
 return Math.floor(Math.random() * (max - min + 1)) + min;_x000D_
}_x000D_
_x000D_
function randomTriangle() {_x000D_
    return {_x000D_
        a: { x: rand(0, W), y: rand(0, H) },_x000D_
        b: { x: rand(0, W), y: rand(0, H) },_x000D_
        c: { x: rand(0, W), y: rand(0, H) }_x000D_
    };_x000D_
}
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>_x000D_
<button id="performance">Run performance test (open console)</button>_x000D_
<pre>Click: place the point._x000D_
Double click: random triangle.</pre>_x000D_
<pre id="result"></pre>_x000D_
<canvas width="500" height="500"></canvas>
_x000D_
_x000D_
_x000D_

Inspired by this: http://www.phatcode.net/articles.php?id=459


C# version of the barycentric method posted by andreasdr and Perro Azul. I added a check to abandon the area calculation when s and t have opposite signs, since potentially avoiding one-third of the multiplication cost seems justified.

Also, even though the math here is firmly-established by now, I ran a thorough unit-test harness on this specific code for good measure anyway.

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

If you are looking for speed, here is a procedure that might help you.

Sort the triangle vertices on their ordinates. This takes at worst three comparisons. Let Y0, Y1, Y2 be the three sorted values. By drawing three horizontals through them you partition the plane into two half planes and two slabs. Let Y be the ordinate of the query point.

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

Costs two more comparisons. As you see, quick rejection is achieved for points outside of the "bounding slab".

Optionally, you can supply a test on the abscissas for quick rejection on the left and on the right (X <= X0' or X >= X2'). This will implement a quick bounding box test at the same time, but you'll need to sort on the abscissas too.

Eventually you will need to compute the sign of the given point with respect to the two sides of the triangle that delimit the relevant slab (upper or lower). The test has the form:

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

The complete discussion of i, j, k combinations (there are six of them, based on the outcome of the sort) is out of the scope of this answer and "left as an exercise to the reader"; for efficiency, they should be hard-coded.

If you think that this solution is complex, observe that it mainly involves simple comparisons (some of which can be precomputed), plus 6 subtractions and 4 multiplies in case the bounding box test fails. The latter cost is hard to beat as in the worst case you cannot avoid comparing the test point against two sides (no method in other answers has a lower cost, some make it worse, like 15 subtractions and 6 multiplies, sometimes divisions).

UPDATE: Faster with a shear transform

As explained just above, you can quickly locate the point inside one of the four horizontal bands delimited by the three vertex ordinates, using two comparisons.

You can optionally perform one or two extra X tests to check insideness to the bounding box (dotted lines).

Then consider the "shear" transform given by X'= X - m Y, Y' = Y, where m is the slope DX/DY for the highest edge. This transform will make this side of the triangle vertical. And since you know on what side of the middle horizontal you are, it suffices to test the sign with respect to a single side of the triangle.

enter image description here

Assuming you precomputed the slope m, as well as the X' for the sheared triangle vertices and the coefficients of the equations of the sides as X = m Y + p, you will need in the worst case

  • two ordinate comparisons for vertical classification;
  • optionally one or two abscissa comparisons for bounding box rejection;
  • computation of X' = X - m Y;
  • one or two comparisons with the abscissas of the sheared triangle;
  • one sign test X >< m' Y + p' against the relevant side of the sheared triangle.

I agree with Andreas Brinck, barycentric coordinates are very convenient for this task. Note that there is no need to solve an equation system every time: just evaluate the analytical solution. Using Andreas' notation, the solution is:

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

where Area is the (signed) area of the triangle:

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

Just evaluate s, t and 1-s-t. The point p is inside the triangle if and only if they are all positive.

EDIT: Note that the above expression for the area assumes that the triangle node numbering is counter-clockwise. If the numbering is clockwise, this expression will return a negative area (but with correct magnitude). The test itself (s>0 && t>0 && 1-s-t>0) doesn't depend on the direction of the numbering, however, since the expressions above that are multiplied by 1/(2*Area) also change sign if the triangle node orientation changes.

EDIT 2: For an even better computational efficiency, see coproc's comment below (which makes the point that if the orientation of the triangle nodes (clockwise or counter-clockwise) is known beforehand, the division by 2*Area in the expressions for s and t can be avoided). See also Perro Azul's jsfiddle-code in the comments under Andreas Brinck's answer.


There are pesky edge conditions where a point is exactly on the common edge of two adjacent triangles. The point cannot be in both, or neither of the triangles. You need an arbitrary but consistent way of assigning the point. For example, draw a horizontal line through the point. If the line intersects with the other side of the triangle on the right, the point is treated as though it is inside the triangle. If the intersection is on the left, the point is outside.

If the line on which the point lies is horizontal, use above/below.

If the point is on the common vertex of multiple triangles, use the triangle with whose center the point forms the smallest angle.

More fun: three points can be in a straight line (zero degrees), for example (0,0) - (0,10) - (0,5). In a triangulating algorithm, the "ear" (0,10) must be lopped off, the "triangle" generated being the degenerate case of a straight line.


Java version of barycentric method:

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

The above code will work accurately with integers, assuming no overflows. It will also work with clockwise and anticlockwise triangles. It will not work with collinear triangles (but you can check for that by testing det==0).

The barycentric version is fastest if you are going to test different points with the same triangle.

The barycentric version is not symmetric in the 3 triangle points, so it is likely to be less consistent than Kornel Kisielewicz's edge half-plane version, because of floating point rounding errors.

Credit: I made the above code from Wikipedia's article on barycentric coordinates.


I wrote this code before a final attempt with Google and finding this page, so I thought I'd share it. It is basically an optimized version of Kisielewicz answer. I looked into the Barycentric method also but judging from the Wikipedia article I have a hard time seeing how it is more efficient (I'm guessing there is some deeper equivalence). Anyway, this algorithm has the advantage of not using division; a potential problem is the behavior of the edge detection depending on 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;
}

In words, the idea is this: Is the point s to the left of or to the right of both the lines AB and AC? If true, it can't be inside. If false, it is at least inside the "cones" that satisfy the condition. Now since we know that a point inside a trigon (triangle) must be to the same side of AB as BC (and also CA), we check if they differ. If they do, s can't possibly be inside, otherwise s must be inside.

Some keywords in the calculations are line half-planes and the determinant (2x2 cross product). Perhaps a more pedagogical way is probably to think of it as a point being inside iff it's to the same side (left or right) to each of the lines AB, BC and CA. The above way seemed a better fit for some optimization however.


Here is a solution in python that is efficient, documented and contains three unittests. It's professional-grade quality and ready to be dropped into your project in the form of a module as is.

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)

There is an additional optional graphical test for the algorithm above to confirm its validity:

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

Producing the following graphic:

Test the point_in_triangle function


I needed point in triangle check in "controlable environment" when you're absolutely sure that triangles will be clockwise. So, I took Perro Azul's jsfiddle and modified it as suggested by coproc for such cases; also removed redundant 0.5 and 2 multiplications because they're just cancel each other.

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

_x000D_
_x000D_
var ctx = $("canvas")[0].getContext("2d");_x000D_
var W = 500;_x000D_
var H = 500;_x000D_
_x000D_
var point = {_x000D_
    x: W / 2,_x000D_
    y: H / 2_x000D_
};_x000D_
var triangle = randomTriangle();_x000D_
_x000D_
$("canvas").click(function (evt) {_x000D_
    point.x = evt.pageX - $(this).offset().left;_x000D_
    point.y = evt.pageY - $(this).offset().top;_x000D_
    test();_x000D_
});_x000D_
_x000D_
$("canvas").dblclick(function (evt) {_x000D_
    triangle = randomTriangle();_x000D_
    test();_x000D_
});_x000D_
_x000D_
test();_x000D_
_x000D_
function test() {_x000D_
    var result = ptInTriangle(point, triangle.a, triangle.b, triangle.c);_x000D_
_x000D_
    var info = "point = (" + point.x + "," + point.y + ")\n";_x000D_
    info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";_x000D_
    info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";_x000D_
    info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";_x000D_
    info += "result = " + (result ? "true" : "false");_x000D_
_x000D_
    $("#result").text(info);_x000D_
    render();_x000D_
}_x000D_
_x000D_
function ptInTriangle(p, p0, p1, p2) {_x000D_
    var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);_x000D_
    var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);_x000D_
_x000D_
    if (s <= 0 || t <= 0) return false;_x000D_
_x000D_
    var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);_x000D_
_x000D_
    return (s + t) < A;_x000D_
}_x000D_
_x000D_
function checkClockwise(p0, p1, p2) {_x000D_
    var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);_x000D_
    return A > 0;_x000D_
}_x000D_
_x000D_
function render() {_x000D_
    ctx.fillStyle = "#CCC";_x000D_
    ctx.fillRect(0, 0, 500, 500);_x000D_
    drawTriangle(triangle.a, triangle.b, triangle.c);_x000D_
    drawPoint(point);_x000D_
}_x000D_
_x000D_
function drawTriangle(p0, p1, p2) {_x000D_
    ctx.fillStyle = "#999";_x000D_
    ctx.beginPath();_x000D_
    ctx.moveTo(p0.x, p0.y);_x000D_
    ctx.lineTo(p1.x, p1.y);_x000D_
    ctx.lineTo(p2.x, p2.y);_x000D_
    ctx.closePath();_x000D_
    ctx.fill();_x000D_
    ctx.fillStyle = "#000";_x000D_
    ctx.font = "12px monospace";_x000D_
    ctx.fillText("1", p0.x, p0.y);_x000D_
    ctx.fillText("2", p1.x, p1.y);_x000D_
    ctx.fillText("3", p2.x, p2.y);_x000D_
}_x000D_
_x000D_
function drawPoint(p) {_x000D_
    ctx.fillStyle = "#F00";_x000D_
    ctx.beginPath();_x000D_
    ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);_x000D_
    ctx.fill();_x000D_
}_x000D_
_x000D_
function rand(min, max) {_x000D_
    return Math.floor(Math.random() * (max - min + 1)) + min;_x000D_
}_x000D_
_x000D_
function randomTriangle() {_x000D_
    while (true) {_x000D_
        var result = {_x000D_
            a: {_x000D_
                x: rand(0, W),_x000D_
                y: rand(0, H)_x000D_
            },_x000D_
            b: {_x000D_
                x: rand(0, W),_x000D_
                y: rand(0, H)_x000D_
            },_x000D_
            c: {_x000D_
                x: rand(0, W),_x000D_
                y: rand(0, H)_x000D_
            }_x000D_
        };_x000D_
        if (checkClockwise(result.a, result.b, result.c)) return result;_x000D_
    }_x000D_
}
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>_x000D_
<pre>Click: place the point._x000D_
Double click: random triangle.</pre>_x000D_
_x000D_
<pre id="result"></pre>_x000D_
_x000D_
<canvas width="500" height="500"></canvas>
_x000D_
_x000D_
_x000D_

Here is equivalent C# code for 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;
}

A simple way is to:

find the vectors connecting the point to each of the triangle's three vertices and sum the angles between those vectors. If the sum of the angles is 2*pi then the point is inside the triangle.

Two good sites that explain alternatives are:

blackpawn and wolfram


Examples related to algorithm

How can I tell if an algorithm is efficient? Find the smallest positive integer that does not occur in a given sequence Efficiently getting all divisors of a given number Peak signal detection in realtime timeseries data What is the optimal algorithm for the game 2048? How can I sort a std::map first by value, then by key? Finding square root without using sqrt function? Fastest way to flatten / un-flatten nested JSON objects Mergesort with Python Find common substring between two strings

Examples related to math

How to do perspective fixing? How to pad a string with leading zeros in Python 3 How can I use "e" (Euler's number) and power operation in python 2.7 numpy max vs amax vs maximum Efficiently getting all divisors of a given number Using atan2 to find angle between two vectors How to calculate percentage when old value is ZERO Finding square root without using sqrt function? Exponentiation in Python - should I prefer ** operator instead of math.pow and math.sqrt? How do I get the total number of unique pairs of a set in the database?

Examples related to geometry

Circle button css Using atan2 to find angle between two vectors How do I compute the intersection point of two lines? Creating a triangle with for loops Plotting a 3d cube, a sphere and a vector in Matplotlib How to find the Center Coordinate of Rectangle? Evenly distributing n points on a sphere How do CSS triangles work? How to draw circle in html page? Generate a random point within a circle (uniformly)