简体   繁体   English

如何检查4个点是否形成凸四边形

[英]How to check if 4 points form a convex quadrilatera

I'm quite new to coding in general.一般来说,我对编码很陌生。 I have found some answers for this question but the answers seem advanced for me.我已经为这个问题找到了一些答案,但这些答案对我来说似乎很先进。

I'm trying to write my own Finite Element Project.我正在尝试编写自己的有限元项目。 For this I would like to write a method that checks if random 4 nodes given as input form a convex quadrilateral.为此,我想编写一个方法来检查作为输入给出的随机 4 个节点是否形成凸四边形。

My method is supposed to look like this:我的方法应该是这样的:

private bool IsConvex(Node[4] corners)
{
    bool isConvex;

    //CODE//

    return isConvex;
}

the Node class is defined by three public properties referring to their coordinates (.coordX, .coordY, .coordZ)节点 class 由三个公共属性定义,参考它们的坐标(.coordX、.coordY、.coordZ)

In order to know if a quadrilateral is convex or not, you can make a triangle of three points and see if the fourth point is located inside that triangle or not.为了知道一个四边形是否是凸的,你可以做一个三点三角形,看看第四个点是否位于该三角形内。 If you manage finding one triangle, which contains the fourth point, then you don't have a convex quadrilateral.如果您设法找到一个包含第四个点的三角形,那么您就没有凸四边形。

Ok, and how can you know if a point is located inside a triangle?好的,你怎么知道一个点是否位于三角形内?

Well, you start by determining at which side a point is located compared to a vector.好吧,您首先确定与向量相比,点位于哪一侧。
Come again?再来?
Well, for each vector, you can find out if a point is located at the left side or at the right side: you just rotate the vector back to the Y-axis, you do the same with the point and if the X coordinate of the point is negative your point is located at the left side, otherwise it's at the right side, like in these three cases (left, left and right):好吧,对于每个向量,您可以确定一个点位于左侧还是右侧:您只需将向量旋转回 Y 轴,您对该点执行相同操作,如果 X 坐标为该点为负,您的点位于左侧,否则位于右侧,如以下三种情况(左、左、右):

在此处输入图像描述

Once you have figured that out, you define a point being inside a triangle if, after having described the triangle as a triangle of vectors, your point is at the same side of all vectors, like in this example (be aware that your triangle consists of the vectors AB, BC and CA: the points must follow up each other):一旦你弄清楚了,你定义一个点在一个三角形内,如果在将三角形描述为一个向量的三角形之后,你的点在所有向量的同一侧,就像在这个例子中一样(注意你的三角形由向量 AB、BC 和 CA 的集合:这些点必须相互跟随):

在此处输入图像描述

Good luck祝你好运

First a little helper class to handle things related to triangles made up of three nodes.首先是一个小帮手 class 来处理与由三个节点组成的三角形相关的事情。

using System.Numerics;

public readonly struct Triangle
{
    public const float DistanceTolerance = 1e-6f;
    public Triangle(Vector3 a, Vector3 b, Vector3 c)
    {
        A = a;
        B = b;
        C = c;
    }

    public Vector3 A { get; }
    public Vector3 B { get; }
    public Vector3 C { get; }

    private Vector3 AreaVector { get => (Vector3.Cross(A, B) + Vector3.Cross(B, C) + Vector3.Cross(C, A)) / 2; }
    public float Area { get => AreaVector.Length(); }
    public Vector3 Normal { get => Vector3.Normalize(AreaVector); }

    public float DistanceTo(Vector3 point) => Vector3.Dot(Normal, point - A);

    public Vector3 Project(Vector3 point)
    {
        // A projected point lies on the plane defined by the three veertices A,B,C
        Vector3 n = Normal;
        float d = Vector3.Dot(n, point - A);
        return point - n * d;
    }

    public void Barycentric(Vector3 P, out (float w_A, float w_B, float w_C) coordinates)
    {
        Vector3 n = Vector3.Cross(A, B) + Vector3.Cross(B, C) + Vector3.Cross(C, A);
        float w_A = Vector3.Dot(n, Vector3.Cross(P, B) + Vector3.Cross(B, C) + Vector3.Cross(C, P));
        float w_B = Vector3.Dot(n, Vector3.Cross(A, P) + Vector3.Cross(P, C) + Vector3.Cross(C, A));
        float w_C = Vector3.Dot(n, Vector3.Cross(A, B) + Vector3.Cross(B, P) + Vector3.Cross(P, A));
        float sum = w_A + w_B + w_C;
        coordinates = (w_A / sum, w_B / sum, w_C / sum);
    }

    public bool Contains(Vector3 P)
    {
        if (Math.Abs(DistanceTo(P)) <= DistanceTolerance)
        {
            Barycentric(P, out var coordinates);
            return coordinates.w_A >= 0 && coordinates.w_A <= 1
                && coordinates.w_B >= 0 && coordinates.w_B <= 1
                && coordinates.w_C >= 0 && coordinates.w_C <= 1;
        }
        return false;
    }
}

If you are not familiar with barycentric coordinates, they are the linear combinations of the vertices that make up an interior (or exterior) point.如果您不熟悉重心坐标,它们是构成内部(或外部)点的顶点的线性组合。

For example if a point is defined as P = 0.3*A + 0.5*B + 0.2*C then the barycentric coordinates of P are (0.3,0.5,0.2) .例如,如果一个点被定义为P = 0.3*A + 0.5*B + 0.2*C那么P的重心坐标是(0.3,0.5,0.2) The only restriction here is that the sum of the barycentric coordinates must equal to 1.这里唯一的限制是重心坐标之和必须等于 1。

A point P is interior to the triangle ABC if all the barycentric coordinates of P are between 0 and 1.如果P的所有重心坐标都在 0 和 1 之间,则点P在三角形ABC的内部。

This is the rule that I am using to write the Triangle.Contains(point) function.这是我用来编写Triangle.Contains(point) function 的规则。 I also check to see if the point is on the same plane as the triangle.我还检查该点是否与三角形在同一平面上。

Now to get to the algorithm to check if an n-gon is convex, all I have to do is take 3 vertices at a time, and check that all remaining other vertices are exterior to those three.现在要使用算法来检查一个 n 边形是否是凸的,我所要做的就是一次取 3 个顶点,并检查所有剩余的其他顶点是否在这三个顶点之外。

    public static bool IsConvex(Vector3[] nodes)
    {            
        for (int i = 0; i < nodes.Length; i++)
        {
            // pick three nodes at a time i,j,k
            var j = (i + 1) % nodes.Length;
            var k = (i + 2) % nodes.Length;
            var A = nodes[i];
            var B = nodes[j];
            var C = nodes[k];

            // deefine triangle ABC from three nodes
            var trig = new Triangle(A, B, C);

            // check nodes after the three and wrap around to grab first nodes also
            for (int r = 3; r < nodes.Length; r++)
            {
                var P = nodes[(r + i) % nodes.Length];

                // if _any_ node is interior to ABC then non-convex
                if (trig.Contains(P))
                {
                    return false;
                }
            }               
        }
        return true;
    }

and some test code to make sure it all works as intended.和一些测试代码,以确保一切都按预期工作。

    static readonly Random rng = new Random();
    static void Main(string[] args)
    {
        // Generate a random 3D triangle
        var trig = new Triangle(
            new Vector3(10 * (float)rng.NextDouble(), 0, 0),
            new Vector3(0, 10 * (float)rng.NextDouble(), 0),
            new Vector3(0, 0, 10 * (float)rng.NextDouble()));

        // Generate an interior point (in the plane)
        var point1 = 0.3f * trig.A + 0.5f * trig.B + 0.2f * trig.C;
        // Check that it is contained inside the triangle
        Debug.Assert(trig.Contains(point1));

        // Generate an exterior point (on the plane)
        var point2 = -0.3f * trig.A + 0.5f * trig.B + 0.8f * trig.C;
        // Check that it is not contained inside the triangle
        Debug.Assert(!trig.Contains(point2));

        // Generate a point out of plane
        var point3 = point1 + 2.5f * trig.Normal;
        // Check that it is not contained inside the triangle
        Debug.Assert(!trig.Contains(point3));

        // Generate a convex quadrilateral
        var poly1 = new Vector3[] {
            new Vector3(0f,0f,0f),
            new Vector3(5f,0f,0f),
            new Vector3(5f,3f,0f),
            new Vector3(1f,7f,0f),
        };

        // Generate a non-convex quadrilateral
        var poly2 = new Vector3[] {
            new Vector3(0f,0f,0f),
            new Vector3(5f,0f,0f),
            new Vector3(2f,2f,0f),
            new Vector3(1f,7f,0f),
        };

        // Check that it is convex
        Debug.Assert(IsConvex(poly1));
        // Check that it is not convex
        Debug.Assert(!IsConvex(poly2));
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM