point-in-triangle
2D-geometry
computational-geometry
triangle-containment
mathematics

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

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

To test whether a point lies inside a 2D triangle, you usually use either barycentric coordinates or cross-product orientation checks. Both are efficient, both work well in code, and both can be adapted to treat points on the edges as either inside or outside depending on your requirements.

Triangle and Point Setup

Assume the triangle vertices are:

  • 'A(ax, ay)'
  • 'B(bx, by)'
  • 'C(cx, cy)'

and the test point is P(px, py).

In code, a convenient representation is:

python
1A = (0.0, 0.0)
2B = (5.0, 0.0)
3C = (2.5, 4.0)
4P = (2.5, 1.5)

The question is whether P lies inside or on the boundary of triangle ABC.

Method 1: Cross-Product Orientation

This is one of the most practical methods. For each edge, compute the signed area-like cross product between the edge vector and the vector from an edge endpoint to P.

python
1def cross(o, a, p):
2    return (a[0] - o[0]) * (p[1] - o[1]) - (a[1] - o[1]) * (p[0] - o[0])
3
4
5def point_in_triangle(p, a, b, c):
6    c1 = cross(a, b, p)
7    c2 = cross(b, c, p)
8    c3 = cross(c, a, p)
9
10    has_neg = (c1 < 0) or (c2 < 0) or (c3 < 0)
11    has_pos = (c1 > 0) or (c2 > 0) or (c3 > 0)
12
13    return not (has_neg and has_pos)
14
15
16print(point_in_triangle(P, A, B, C))

If the cross products all have the same sign, or some are zero and the rest share a sign, the point is inside or on an edge.

Why the Cross-Product Test Works

Each cross product tells you which side of an edge the point is on. A point inside a triangle must lie consistently on the same side of all three directed edges. If the signs conflict, the point is outside.

This method is fast and avoids computing areas directly.

Method 2: Barycentric Coordinates

Barycentric coordinates express P as a weighted combination of A, B, and C. If the weights are all between 0 and 1 and sum to 1, the point is inside the triangle.

python
1def point_in_triangle_barycentric(p, a, b, c):
2    denom = ((b[1] - c[1]) * (a[0] - c[0]) +
3             (c[0] - b[0]) * (a[1] - c[1]))
4
5    if denom == 0:
6        return False  # Degenerate triangle
7
8    w1 = ((b[1] - c[1]) * (p[0] - c[0]) +
9          (c[0] - b[0]) * (p[1] - c[1])) / denom
10
11    w2 = ((c[1] - a[1]) * (p[0] - c[0]) +
12          (a[0] - c[0]) * (p[1] - c[1])) / denom
13
14    w3 = 1 - w1 - w2
15
16    return 0 <= w1 <= 1 and 0 <= w2 <= 1 and 0 <= w3 <= 1
17
18
19print(point_in_triangle_barycentric(P, A, B, C))

This method is especially useful if you also want interpolation weights for graphics, physics, or finite-element style calculations.

Degenerate Triangles

Both methods assume the triangle has nonzero area. If the three vertices are collinear, the "triangle" is degenerate and the usual inside test is not meaningful without extra line-segment logic.

A simple area check helps:

python
1def triangle_area2(a, b, c):
2    return abs(cross(a, b, c))
3
4print(triangle_area2(A, B, C))

If the doubled area is zero, you should handle the case separately.

Boundary Behavior

The word "inside" often hides an important policy choice:

  • should points on an edge count as inside?
  • should a vertex count as inside?

The cross-product example above treats boundary points as inside because zero cross products are allowed. If you need strictly interior points only, require all three signs to be strictly positive or strictly negative.

Numerical Precision

For floating-point coordinates, points very close to an edge may produce tiny numerical errors. A small epsilon tolerance can make the result more stable:

python
1EPS = 1e-9
2
3def non_negative(x):
4    return x >= -EPS

Then use tolerance-aware comparisons instead of exact >= 0.

For integer-coordinate geometry, this issue is much smaller because the cross-product values are exact integers instead of rounded floating-point results.

Common Pitfalls

The biggest mistake is forgetting to handle degenerate triangles. If the three vertices are collinear, barycentric formulas can divide by zero and orientation checks may give misleading results.

Another issue is not deciding what to do with boundary points. Many bugs are really policy mismatches, where one part of the system considers edge points inside and another considers them outside.

Finally, be careful with floating-point precision near edges and vertices. If geometric data comes from transforms or user input, exact comparisons can become unstable unless you introduce a small tolerance.

Summary

  • Cross-product orientation is a fast and practical point-in-triangle test.
  • Barycentric coordinates are also effective and useful when interpolation weights are needed.
  • Degenerate triangles must be handled separately.
  • Decide explicitly whether edge and vertex points count as inside.
  • Use an epsilon tolerance when working with floating-point geometry.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.