Search code examples
c#unity-game-enginepolygon

Point in Polygon including edge cases with margin in c#


I am using Unity3D and I have a polygon (Vector2 array) as well as points to check. For the last few days I was searching for solutions including pnpoly and other algorithms. The problem is I have an inaccuracy of up to 0.001f because I project 3D Faces onto a 2D plane by multiplying with a Quaternion right after using TransformPoint (to get the world position of a mesh-vertex).

I don't know anything about the polygons because they are formed from a number of mesh-triangles - they can have any shape.

How could I deal with this extreme inaccuracy and find all points that are inside or on the boundaries of the polygon?

    public static bool IsInsidePolygon(Vector2[] vertices, Vector2 checkPoint)
    {
        float[] vertX = new float[vertices.Length];
        float[] vertY = new float[vertices.Length];
        for (int i = 0; i < vertices.Length; i++)
        {
            vertX[i] = vertices[i].x;
            vertY[i] = vertices[i].y;
        }

        return IsInsidePolygon(vertices.Length, vertX, vertY, checkPoint.x, checkPoint.y);
    }

    public static bool IsInsidePolygon3(int nvert, float[] vertx, float[] verty, float testx, float testy)
    {
        int i, j = 0;
        bool c = false;
        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if (((verty[i] > testy) != (verty[j] > testy)) &&
         (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                c = !c;
        }
        return c;
    }

Solution

  • The solution was to find the Closest Distance to the Polygon and return true if the distance is within the margin, here is the whole code:

    '''

    public static float DistancePointLine2D(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        return (ProjectPointLine2D(point, lineStart, lineEnd) - point).magnitude;
    }
    public static Vector2 ProjectPointLine2D(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        Vector2 rhs = point - lineStart;
        Vector2 vector2 = lineEnd - lineStart;
        float magnitude = vector2.magnitude;
        Vector2 lhs = vector2;
        if (magnitude > 1E-06f)
        {
            lhs = (Vector2)(lhs / magnitude);
        }
        float num2 = Mathf.Clamp(Vector2.Dot(lhs, rhs), 0f, magnitude);
        return (lineStart + ((Vector2)(lhs * num2)));
    }
    
    
    public static float ClosestDistanceToPolygon(Vector2[] verts, Vector2 point)
    {
        int nvert = verts.Length;
        int i, j = 0;
        float minDistance = Mathf.Infinity;
        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            float distance = DistancePointLine2D(point, verts[i], verts[j]);
            minDistance = Mathf.Min(minDistance, distance);
        }
    
        return minDistance;
    }
    
    public static bool IsInsidePolygon(Vector2[] vertices, Vector2 checkPoint, float margin = 0.01f)
    {
        if(ClosestDistanceToPolygon(vertices, checkPoint) < margin)
        {
            return true;
        }
    
        float[] vertX = new float[vertices.Length];
        float[] vertY = new float[vertices.Length];
        for (int i = 0; i < vertices.Length; i++)
        {
            vertX[i] = vertices[i].x;
            vertY[i] = vertices[i].y;
        }
    
        return IsInsidePolygon(vertices.Length, vertX, vertY, checkPoint.x, checkPoint.y);
    }
    
    public static bool IsInsidePolygon(int nvert, float[] vertx, float[] verty, float testx, float testy)
    {
        bool c = false;
        int i, j = 0;
        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if ((((verty[i] <= testy) && (testy < verty[j])) ||
    
                 ((verty[j] <= testy) && (testy < verty[i]))) &&
    
                (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                c = !c;
        }
        return c;
    }
    

    '''