This is a collection of useful algorithms that are so simple they dont deserve their own page.
//Where is p in relation to a-b
// < 0 -> to the right
// = 0 -> on the line
// > 0 -> to the left
public static float IsAPointLeftOfVectorOrOnTheLine(Vector2 a, Vector2 b, Vector2 p)
{
float determinant = (a.x - p.x) * (b.y - p.y) - (a.y - p.y) * (b.x - p.x);
return determinant;
}
//Clamp list indices
//Will even work if index is larger/smaller than listSize, so can loop multiple times
public static int ClampListIndex(int index, int listSize)
{
index = ((index % listSize) + listSize) % listSize;
return index;
}
//Is a triangle in 2d space oriented clockwise or counter-clockwise
//https://math.stackexchange.com/questions/1324179/how-to-tell-if-3-connected-points-are-connected-clockwise-or-counter-clockwise
//https://en.wikipedia.org/wiki/Curve_orientation
public static bool IsTriangleOrientedClockwise(Vector2 p1, Vector2 p2, Vector2 p3)
{
bool isClockWise = true;
float determinant = p1.x * p2.y + p3.x * p1.y + p2.x * p3.y - p1.x * p3.y - p3.x * p2.y - p2.x * p1.y;
if (determinant > 0f)
{
isClockWise = false;
}
return isClockWise;
}
//From http://totologic.blogspot.se/2014/01/accurate-point-in-triangle-test.html
//p is the testpoint, and the other points are corners in the triangle
public static bool IsPointInTriangle(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p)
{
bool isWithinTriangle = false;
//Based on Barycentric coordinates
float denominator = ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y));
float a = ((p2.y - p3.y) * (p.x - p3.x) + (p3.x - p2.x) * (p.y - p3.y)) / denominator;
float b = ((p3.y - p1.y) * (p.x - p3.x) + (p1.x - p3.x) * (p.y - p3.y)) / denominator;
float c = 1 - a - b;
//The point is within the triangle or on the border if 0 <= a <= 1 and 0 <= b <= 1 and 0 <= c <= 1
//if (a >= 0f && a <= 1f && b >= 0f && b <= 1f && c >= 0f && c <= 1f)
//{
// isWithinTriangle = true;
//}
//The point is within the triangle
if (a > 0f && a < 1f && b > 0f && b < 1f && c > 0f && c < 1f)
{
isWithinTriangle = true;
}
return isWithinTriangle;
}
//http://thirdpartyninjas.com/blog/2008/10/07/line-segment-intersection/
public static bool AreLinesIntersecting(Vector2 l1_p1, Vector2 l1_p2, Vector2 l2_p1, Vector2 l2_p2, bool shouldIncludeEndPoints)
{
bool isIntersecting = false;
float denominator = (l2_p2.y - l2_p1.y) * (l1_p2.x - l1_p1.x) - (l2_p2.x - l2_p1.x) * (l1_p2.y - l1_p1.y);
//Make sure the denominator is > 0, if not the lines are parallel
if (denominator != 0f)
{
float u_a = ((l2_p2.x - l2_p1.x) * (l1_p1.y - l2_p1.y) - (l2_p2.y - l2_p1.y) * (l1_p1.x - l2_p1.x)) / denominator;
float u_b = ((l1_p2.x - l1_p1.x) * (l1_p1.y - l2_p1.y) - (l1_p2.y - l1_p1.y) * (l1_p1.x - l2_p1.x)) / denominator;
//Are the line segments intersecting if the end points are the same
if (shouldIncludeEndPoints)
{
//Is intersecting if u_a and u_b are between 0 and 1 or exactly 0 or 1
if (u_a >= 0f && u_a <= 1f && u_b >= 0f && u_b <= 1f)
{
isIntersecting = true;
}
}
else
{
//Is intersecting if u_a and u_b are between 0 and 1
if (u_a > 0f && u_a < 1f && u_b > 0f && u_b < 1f)
{
isIntersecting = true;
}
}
}
return isIntersecting;
}
...and if we know they are intersecting it might be useful to know the coordinate of the intersection point
//Whats the coordinate of an intersection point between two lines in 2d space if we know they are intersecting
//http://thirdpartyninjas.com/blog/2008/10/07/line-segment-intersection/
public static Vector2 GetLineLineIntersectionPoint(Vector2 l1_p1, Vector2 l1_p2, Vector2 l2_p1, Vector2 l2_p2)
{
float denominator = (l2_p2.y - l2_p1.y) * (l1_p2.x - l1_p1.x) - (l2_p2.x - l2_p1.x) * (l1_p2.y - l1_p1.y);
float u_a = ((l2_p2.x - l2_p1.x) * (l1_p1.y - l2_p1.y) - (l2_p2.y - l2_p1.y) * (l1_p1.x - l2_p1.x)) / denominator;
Vector2 intersectionPoint = l1_p1 + u_a * (l1_p2 - l1_p1);
return intersectionPoint;
}
//Is a point to the left, to the right, or on a plane
//https://gamedevelopment.tutsplus.com/tutorials/understanding-sutherland-hodgman-clipping-for-physics-engines--gamedev-11917
//Notice that the plane normal doesnt have to be normalized
public static float DistanceFromPointToPlane(Vector3 planeNormal, Vector3 planePos, Vector3 pointPos)
{
//Positive distance denotes that the point p is on the front side of the plane
//Negative means it's on the back side
float distance = Vector3.Dot(planeNormal, pointPos - planePos);
return distance;
}
//Get the coordinate if we know a ray-plane is intersecting
public static Vector3 GetRayPlaneIntersectionCoordinate(Vector3 planePos, Vector3 planeNormal, Vector3 rayStart, Vector3 rayDir)
{
float denominator = Vector3.Dot(-planeNormal, rayDir);
Vector3 vecBetween = planePos - rayStart;
float t = Vector3.Dot(vecBetween, -planeNormal) / denominator;
Vector3 intersectionPoint = rayStart + rayDir * t;
return intersectionPoint;
}
//Is a line-plane intersecting?
public static bool AreLinePlaneIntersecting(Vector3 planeNormal, Vector3 planePos, Vector3 linePos1, Vector3 linePos2)
{
bool areIntersecting = false;
Vector3 lineDir = (linePos1 - linePos2).normalized;
float denominator = Vector3.Dot(-planeNormal, lineDir);
//No intersection if the line and plane are parallell
if (denominator > 0.000001f || denominator < -0.000001f)
{
Vector3 vecBetween = planePos - linePos1;
float t = Vector3.Dot(vecBetween, -planeNormal) / denominator;
Vector3 intersectionPoint = linePos1 + lineDir * t;
if (IsPointBetweenPoints(linePos1, linePos2, intersectionPoint))
{
areIntersecting = true;
}
}
return areIntersecting;
}
...which requires that we know how to tell if a point is between two points:
//Is a point c between point a and b (we assume all 3 are on the same line)
public static bool IsPointBetweenPoints(Vector3 a, Vector3 b, Vector3 c)
{
bool isBetween = false;
//Entire line segment
Vector3 ab = b - a;
//The intersection and the first point
Vector3 ac = c - a;
//Need to check 2 things:
//1. If the vectors are pointing in the same direction = if the dot product is positive
//2. If the length of the vector between the intersection and the first point is smaller than the entire line
if (Vector3.Dot(ab, ac) > 0f && ab.sqrMagnitude >= ac.sqrMagnitude)
{
isBetween = true;
}
return isBetween;
}
...and we might also need to know the point of intersection if we know they are intersecting:
//We know a line plane is intersecting and now we want the coordinate of intersection
public static Vector3 GetLinePlaneIntersectionCoordinate(Vector3 planeNormal, Vector3 planePos, Vector3 linePos1, Vector3 linePos2)
{
Vector3 vecBetween = planePos - linePos1;
Vector3 lineDir = (linePos1 - linePos2).normalized;
float denominator = Vector3.Dot(-planeNormal, lineDir);
float t = Vector3.Dot(vecBetween, -planeNormal) / denominator;
Vector3 intersectionPoint = linePos1 + lineDir * t;
return intersectionPoint;
}
//The list describing the polygon has to be sorted either clockwise or counter-clockwise because we have to identify its edges
public static bool IsPointInPolygon(List<Vector2> polygonPoints, Vector2 point)
{
//Step 1. Find a point outside of the polygon
//Pick a point with a x position larger than the polygons max x position, which is always outside
Vector2 maxXPosVertex = polygonPoints[0];
for (int i = 1; i < polygonPoints.Count; i++)
{
if (polygonPoints[i].x > maxXPosVertex.x)
{
maxXPosVertex = polygonPoints[i];
}
}
//The point should be outside so just pick a number to make it outside
Vector2 pointOutside = maxXPosVertex + new Vector2(10f, 0f);
//Step 2. Create an edge between the point we want to test with the point thats outside
Vector2 l1_p1 = point;
Vector2 l1_p2 = pointOutside;
//Step 3. Find out how many edges of the polygon this edge is intersecting
int numberOfIntersections = 0;
for (int i = 0; i < polygonPoints.Count; i++)
{
//Line 2
Vector2 l2_p1 = polygonPoints[i];
int iPlusOne = ClampListIndex(i + 1, polygonPoints.Count);
Vector2 l2_p2 = polygonPoints[iPlusOne];
//Are the lines intersecting?
if (AreLinesIntersecting(l1_p1, l1_p2, l2_p1, l2_p2, true))
{
numberOfIntersections += 1;
}
}
//Step 4. Is the point inside or outside?
bool isInside = true;
//The point is outside the polygon if number of intersections is even or 0
if (numberOfIntersections == 0 || numberOfIntersections % 2 == 0)
{
isInside = false;
}
return isInside;
}