点到线段的最近距离, 点与线段的位置关系 - 点乘投影方式

点到线段的最近距离

判断依据

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与ab共线

 

//线段上离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;
}

求最近距离

//点到线段的距离, flag: -1_表示最近点为a, 1_表示最近点为b, 0_表示最近点为线段中间的点
public static float PointToSegmentDistance2(Vector2 a, Vector2 b, Vector2 p, out int flag)
{
    var c = GetSegmentClosestPoint(a, b, p, out flag);
    var pc = c - p;
    return pc.magnitude;
}

 

 

点与线段的位置关系

判断依据:投影结果<0或>线段ab的长度,则在线段的两侧;否则在线段内侧

//点与线段的位置关系: -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;
}

 

posted @ 2023-12-21 23:31  yanghui01  阅读(34)  评论(0编辑  收藏  举报