检查投影在线段上的点是否不在其外部

时间:2013-07-10 22:04:06

标签: java math geometry computational-geometry

illustration

见上图;基本上,我想要一个简单的测试来检查一个点是否在线段的范围内。我有的信息(或输入,如果您愿意)是点的坐标和线段终点的坐标。我想要的输出是一个简单的布尔值。我怎样才能以简单的方式检查这个?

4 个答案:

答案 0 :(得分:11)

如果您使用内部产品,您可以进行简单统一的检查。两个矢量之间的内积可以几何可视化为两个矢量的长度乘以两者之间的角度的余弦的乘积,或者一个矢量的长度与(正交)投影的长度的乘积。另一个在由该向量确定的行上。

在您的情况下,如果您将矢量v从段的一个端点投影到所考虑的点,则该点位于允许的区域内,当且仅当投影落在段{{ 1}}连接两个端点。这相当于

s

(严格的不等式,如果你想通过端点排除垂直于线段的线)

0 <= v·s <= s·s

答案 1 :(得分:2)

通过粗线的端点确定那些垂直虚线的方程并不难。

让粗线由点(x1, y1)(x2, y2)定义。然后,它有一个斜率

m = (y2 - y1) / (x2 - x1)

因此所有垂直线的形式都是

y(x) = (-1/m)x + c

我们可以用它来确定通过(x1, y1)(x2, y2)(分别)的垂直线的方程,它基本上代表了所有有效点必须驻留的区域的边界: / p>

ya(x) = (-1/m)x + y1 + x1/m
yb(x) = (-1/m)x + y2 + x2/m

因此,对于任意点(x*, y*),要确定它是否位于有效区域,您可以测试是否

ya(x*) <= y* <= yb(x*)

(如果ya(x*)更大,则相反)


以下应该可以解决问题:

public static boolean check(double x1, double y1, double x2, double y2,
            double x, double y) {

    if (x1 == x2) {  // special case
        return y1 < y2 ? (y1 <= y && y <= y2) : (y2 <= y && y <= y1);
    }

    double m = (y2 - y1) / (x2 - x1);
    double r1 = x1 + m * y1;
    double r2 = x2 + m * y2;
    double r = x + m * y;
    return r1 < r2 ? (r1 <= r && r <= r2) : (r2 <= r && r <= r1);
}

答案 2 :(得分:1)

你可以通过计算角度来做到这一点。

假设你的端点是(x1,y1)和(x2,y2),你的点是(x,y)。

然后创建两个向量,从一个端点到另一个端点,以及一个端点到您的点:

vec1 = (x - x1, y - y1);
vec2 = (x2 - x1, y2 - y1);

计算点积:

double dotp = (x-x1) * (x2-x1) + (y-y1) * (y2 - y1);

现在,除以幅度的点积为您提供角度的余弦:

double theta = Math.acos((dtop) / (Math.sqrt((x-x1) * (x-x1) + (y-y1) * (y-y1)) 
       * Math.sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1))));

现在的诀窍是,如果你的角度大于PI / 2,你就是'出局'

public static boolean check(double x, double y, double x1, double y1, 
                            double x2, double y2) {
    // vectors are (dx1, dy1) and (dx2, dy2)
    double dx1 = x - x1, dx2 = x2 - x1, dy1 = y - y1, dy2 = y2 - y1;

    double dotp = dx1 * dx2 + dy1 * dy2;
    double theta = Math.acos(dotp  / (Math.sqrt(dx1 * dx1 + dy1 * dy1) 
                                      * Math.sqrt(dx2 * dx2 + dy2 * dy2)));
    theta = Math.abs(theta);

    if (theta > (Math.PI / 2))
        return false;
    dx1 = x - x2;
    dx2 = x1 - x2;
    dy1 = y - y2;
    dy2 = y1 - y2;
    dotp = dx1 * dx2 + dy1 * dy2;
    theta = Math.acos(dotp  / (Math.sqrt(dx1 * dx1 + dy1 * dy1) 
                               * Math.sqrt(dx2 * dx2 + dy2 * dy2)));
    theta = Math.abs(theta);

    if (theta > (Math.PI / 2))
        return false;
    return true;
}

答案 3 :(得分:0)

我接受了Daniel Fischer的回答,这很好,并为3D和Unity进行了调整:

public bool InSegmentRange(Vector3 start, Vector3 end, Vector3 point) {
    Vector3 delta = end - start;
    float innerProduct = (point.x - start.x) * delta.x + (point.y - start.y) * delta.y + (point.z - start.z) * delta.z;
    return innerProduct >= 0 && innerProduct <= delta.x * delta.x + delta.y * delta.y + delta.z * delta.z;
}