线段上离p最近的点 - 投影方式

发布时间 2023-12-21 23:37:31作者: yanghui01

判断依据

1) 投影结果<0,则线段端点a离p最近

2) 投影结果>线段ab的长度,则线段端点b离p最近

3) 否则p在线段上的垂点为最近点

 

p与ab不共线时

1) p在线段两侧

2-a) p在线段内侧

2-b) p在线段内侧2

 

p与ab共线时

1) p在线段两侧

 

2-a) p在线段内侧

2-b) p在线段内侧

 

//线段上离p最近的点, flag: -1_表示最近点为a, 1_表示最近点为b, 0_表示最近点为线段中间的点
public static Vector2 GetSegmentClosestPoint(Vector2 a, Vector2 b, Vector2 p, out int flag)
{
    flag = 0;
    var ab = b - a;

    float abSqrLen = ab.sqrMagnitude;
    if (abSqrLen < float.Epsilon) //ab重合
        return a;

    var ap = p - a;
    // 计算投影长度,并单位化到[0, 1](需要额外除以ab的长度)。因此这里就直接除以长度的平方了。
    float proj = Vector2.Dot(ap, ab) / abSqrLen;
    if (proj < 0)
    {
        flag = -1;
        return a;
    }
    if (proj > 1)
    {
        flag = 1;
        return b;
    }

    return a + ab * proj;
}

 

这个方法同样也能用来判断点与线段的位置关系

//点与线段的位置关系: -1_外侧, 0_与端点重合, 1_内侧; isA: 是否点A那边的外侧或是否和点A重合
public static int GetPointSideOfSegment2(Vector2 p, Vector2 a, Vector2 b, out bool isA)
{
    isA = false;

    var ab = b - a;
    var ap = p - a;

    float abSqrLen = ab.sqrMagnitude;
    // 计算投影长度,并单位化到[0, 1](需要额外除以ab的长度)。因此这里就直接除以长度的平方了。
    float proj = Vector2.Dot(ap, ab) / abSqrLen;
    if (proj < 0)
    {
        isA = true;
        return -1;
    }
    if (proj > 1)
    {
        return -1;
    }

    //点和线段端点重合时
    float apSqrLen = ap.sqrMagnitude;
    if (apSqrLen < float.Epsilon)
    {
        isA = true;
        return 0;
    }

    var bp = p - b;
    float bpSqrLen = bp.sqrMagnitude;
    if (bpSqrLen < float.Epsilon)
        return 0;

    return 1;
}