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;
In order to have the point in the triangle we have to satisfy 3 conditions
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;
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_
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:
By using the analytic solution to the barycentric coordinates (pointed out by Andreas Brinck) and:
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
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_
Leading to:
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:
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-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.
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/
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_
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.
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 trianglespointInTriangle(p, p0, p1, p2)
- for clockwise trianglesLook in jsFiddle (performance test included), there's also winding checking in a separate function. Or press "Run code snippet" below
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_
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.
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
X' = X - m Y
;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:
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/
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_
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;
}
Source: Stackoverflow.com