点至三角形的最近点


点P到三角形的相交点分为三种情况:
1.顶点域,相交点为三角形的三个顶点
2.边域,相交点在三角形的三条边上,也就是点P在边上的投影
3.中心域,相交点在三角形内部,也就是点P在三角形中的投影

代码如下:

已优化cross为dot的版本

    /// <summary>
    /// 点到三角形最近点,已优化cross为dot
    /// 求解思路:平面的法线和pa,pb,pc的法线进行dot,可知p是落在哪个域上,再对p在各个边上进行投影,求得边域上最近点。在三角形内就是质心。在顶点域就是顶点
    /// 优化过程:
    /// 根据拉格朗日恒等式:
    //(axb)·(cxd)=(a·c)(b·d)-(a·d)(b·c)

    //可表示3个标量三重积:
    //Vector n = Cross(b – a, c – a);
    //        float va = Dot(n, Cross(b – p, c – p));
    //        float vb = Dot(n, Cross(c – p, a – p));
    //        float vc = Dot(n, Cross(a – p, b – p));

    //        并利用6个点积计算对以下内容进行表达:
    //float d1 = Dot(b – a, p – a);
    //        float d2 = Dot(c – a, p – a);
    //        float d3 = Dot(b – a, p – b);
    //        float d4 = Dot(c – a, p – b);
    //        float d5 = Dot(b – a, p – c);
    //        float d6 = Dot(c – a, p – c);

    //        最终有:
    //float va = d3 * d6 – d5* d4;
    //        float vb = d5 * d2 – d1* d6;
    //        float vc = d1 * d4 – d3* d2;

    //        实际上,d1 ~d6也可以用于计算snom、sdenom、tnom、tdenom、unom以及udenom项:
    //float snom = d1;
    //        float sdenom = -d3;
    //        float tnom = d2;
    //        float tdenom = -d6;
    //        float unom = d4 – d3;
    //float udenom = d5 – d6;
    /// </summary>
    /// <param name="p"></param>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <param name="c"></param>
    /// <returns></returns>
    public static Vector2 CloestPointTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
    {
        Vector2 ab = b - a;
        Vector2 ac = c - a;
        Vector2 ap = p - a;
        float d1 = Vector2.Dot(ab, ap);
        float d2 = Vector2.Dot(ac, ap);
        if (d1 <= 0f && d2 <= 0f)
            return a;

        Vector2 bp = p - b;
        float d3 = Vector2.Dot(ab, bp);
        float d4 = Vector2.Dot(ac, bp);
        if (d3 >= 0f && d4 <= d3)
            return b;

        Vector2 cp = p - c;
        float d5 = Vector2.Dot(ab, cp);
        float d6 = Vector2.Dot(ac, cp);
        if (d6 >= 0f && d5 < d6)
            return c;

        float vc = d1 * d4 - d3 * d2;
        if (vc <= 0f && d1 >= 0f && d3 <= 0f)
        {
            float v1 = d1 / (d1 - d3);
            return a + v1 * ab;
        }

        float vb = d5 * d2 - d1 * d6;
        if (vb <= 0f && d2 >= 0f && d6 <= 0f)
        {
            float w1 = d2 / (d2 - d6);
            return a + w1 * ac;
        }

        float va = d3 * d6 - d5 * d4;
        if (va <= 0f && (d4 - d3) >= 0f && (d5 - d6) >= 0f)
        {
            float w1 = (d4 - d3) / ((d4 - d3) + (d5 - d6));
            return b + w1 * (c - b);
        }

        float denom = 1f / (va + vb + vc);
        float v = vb * denom;
        float w = vc * denom;
        return a + ab * v + ac * w;
    }

未优化版本

    / <summary>
    / 求点到三角形最近点,未优化cross
    / </summary>
    / <param name="a"></param>
    / <param name="b"></param>
    / <param name="c"></param>
    / <param name="p"></param>
    / <returns></returns>
    //public static Vector3 CloestPointTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 p)
    //{
    //    Vector3 ab = b - a;
    //    Vector3 ac = c - a;
    //    Vector3 bc = c - b;

    //    float snom = Vector3.Dot(p - a, ab);// pa与ab的夹角
    //    float sdenom = Vector3.Dot(p - b, a - b);//ba和bp的夹角
    //    float tnom = Vector3.Dot(p - a, ac);//pa与ac的夹角
    //    float tdenom = Vector3.Dot(p - c, a - c);//ca和cp的夹角
    //    // 顶点域:
    //    // pa与ab的夹角大于90度,并且pa与ac的夹角大于90度时,最近点为a
    //    if (snom <= 0.0f && tnom <= 0.0f)
    //        return a;

    //    float unom = Vector3.Dot(p - b, bc);//bp和bc的夹角
    //    float udenom = Vector3.Dot(p - c, b - c);//cb和cp的家教
    //    if (sdenom <= 0.0f && unom <= 0.0f)
    //        return b;
    //    if (tdenom <= 0.0f && udenom <= 0.0f)
    //        return c;

    //    Vector3 n = Vector3.Cross(b - a, c - a);
    //    float vc = Vector3.Dot(n, Vector3.Cross(a - p, b - p));
    //    // 边域:
    //    // vc <= 0 ,因此p在于ab的外部,并且p位于ab的中间,
    //    // 因此最近点位于ab边域上,然后根据点乘投影的比例,可求出点p在ab上的投影则为最近点
    //    if (vc <= 0.0f && snom >= 0.0f && sdenom >= 0.0f)
    //        return a + snom / (snom + sdenom) * ab;

    //    float va = Vector3.Dot(n, Vector3.Cross(b - p, c - p));
    //    if (va <= 0.0f && unom >= 0.0f && udenom >= 0.0f)
    //        return b + unom / (unom + udenom) * bc;

    //    float vb = Vector3.Dot(n, Vector3.Cross(c - p, a - p));
    //    if (vb <= 0.0f && tnom >= 0.0f && tdenom >= 0.0f)
    //        return a + tnom / (tnom + tdenom) * ac;

    //    // 中心域:
    //    // 最近点在abc的中心域,利用质心表达式和面积之比求出p点在abc中的投影
    //    float u = va / (va + vb + vc);
    //    float v = vb / (va + vb + vc);
    //    float w = 1.0f - u - v;
    //    return u * a + v * b + w * c;
    //}
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页