发现两条线段之间的距离?

问题描述 投票:2回答:4

给定两个线段,找到该线段之间的距离是d的两个点。

这是类似于“之间的两个线段问题最短距离”,除了我们正在求解上由给定的距离d分开的线段的两个点。

每个线段由两个3维点。

我已经通过谷歌找到了数学同时搜索恐慌和迷惑我。我是一个程序员,但我很难理解的证据和分析解决背后的这样一个问题。

输入:2条线段和距离d

输出:2点上的每个段是从彼此相距一定距离d,或无如果不存在两个点

algorithm math 3d
4个回答
1
投票

这是一种非迭代求解。我担心,数学会刺激你,虽然这里没有什么复杂的。

首先最容易与距离工作贯穿平方。

它一个threeD线通过点P和Q,以及其他由点R和S所描述的,然后说明该问题的一种方式是,我们希望找到标量m和n,使得距离的点之间的平方的馏分米沿第一行,和一个点的馏分ñ沿着第二是给定DSQ。

我们必须限制m和n为1(含)之间的0和,使我们的点是真正的线段。

如果有m和n,则点

X = P + m*(Q-P)
Y = R + n*(S-R)

假设我们是第一个发现DSQ的最小值和最大值。这将告诉我们是否有一个解决方案:如果DSQ的给定值是小于最小值或大于最大值,没有解决方案,我们可以停止。

让m和n为在其中发生最小是M_MIN和转速存取,以及那些用于最大是m_max所和N_MAX点值。如果我们引入一个新的变量z(在[0,1]),那么我们可以考虑一个的M“线”,N,值:

m(z) = m_min + z*(m_max-m_min)
n(z) = n_min + z*(n_max-n_min)

当z是0时,这些都为最小DSQ的值,而对于Z = 1,它们是用于maximim DSQ。因此,当我们从0到1增加Z,DSQ的价值必须经过我们想要的价值!也就是说,我们只需要搜索的z,使得DSQ是我们想要的价值的价值。

是什么使问题(相对)直接的是,X和Y之间的distanceSquared是m和n中的二阶多项式。具体而言,一些繁琐的代数表明,如果DSQ是X和Y之间的平方距离,

Dsq = a + 2*b*m + 2*c*m + d*m*m + 2*e*m*n + f*m*m
where, in terms of dot products
a = (P-R).(P-R)
b = (P-R).(Q-P)
c =-(P-R).(S-R)
d = (Q-P).(Q-P)
e =-(Q-P).(S-R)
f = (S-R).(S-R)

的最大值和最小值必须发生在角部的(())M,N)=(0,0)或(0,1或(1,0或(1,1))的任一个或沿着边缘中的一个(在(0,N)或(1,n)的某些n或(M,0)或(M,1)的一些米),或者在中间的一个点,其中DSQ(的衍生物相对于m和N)均为0)。注:例如,关于边缘说(0,N),我们得到一个二次n个DSQ为,所以很容易找到的最大的那个。

更进一步,当我们走到一起的最小值和最大值之间的“行”看,如果我们替换M(z)和N(Z),成为我们的DSQ公式,我们得到的,更繁琐的代数后,沿z二次,所以很容易找到z的值,这将使DSQ的期望值。

那么,这个帖子已经是相当长的,所以这里的实现这些想法的C代码。我尝试了百万随机值的点,当距离总是最大和最小之间,它总能找到合适的三维点。在我(相当普通)Linux桌面这花了几秒钟。

   //   3d vectors
static  void    v3_sub( double* P, double* Q, double* D)
{   D[0] = P[0]-Q[0];
    D[1] = P[1]-Q[1];
    D[2] = P[2]-Q[2];
}
static  double  v3_dot( double* P, double* Q)
{   return P[0]*Q[0] + P[1]*Q[1] + P[2]*Q[2];
}

//  quadratic in one variable
// return *x so X -> r[0] + 2*r[1]*X + r[2]*X*X has minumum at *x
static  int quad_min( const double*r, double* x)
{   if ( r[2] <= 0.0)
    {   return 0;
    }
    *x = -r[1]/r[2];
    return 1;
}

// return x so r[0] + 2*r[1]*x + r[2]*x*x == d, and whether 0<=x<=1
static  int solve_quad( const double* r, double d, double* x)
{
double  ap = r[0] - d;
    if ( r[1] > 0.0)
    {
    double  root1 = -(r[1] + sqrt( r[1]*r[1] - ap*r[2]));   // < 0 
        *x = ap/root1;
    }
    else
    {
    double  root1 = (-r[1] + sqrt( r[1]*r[1] - ap*r[2]));   // >= 0
        if ( root1 < r[2])
        {   *x = root1/r[2];
        }
        else
        {   *x = ap/root1;
        }
    }
    return 0.0 <= *x && *x <= 1.0;
}

//  quadratic in 2 variables
typedef struct
{   double  a,b,c,d,e,f;
}   quad2T;

static  double  eval_quad2( const quad2T* q, double m, double n)
{
    return  q->a
    +   2.0*(m*q->b + n*q->c)
    +   m*m*q->d + 2.0*m*n*q->e + n*n*q->f
    ;
}

// eval coeffs of quad2 so that quad2(m,n) = distsq( P+m*(Q-P), R+n*(S-R))
static  quad2T  set_quad2( double* P, double* Q, double* R, double* S)
{
double  D[3];   v3_sub( P, R, D);
double  U[3];   v3_sub( Q, P, U);
double  V[3];   v3_sub( S, R, V);
quad2T  q;
    // expansion of lengthSq( D+m*U-n*V)
    q.a = v3_dot( D, D);
    q.b = v3_dot( D, U);
    q.c = -v3_dot( D, V);
    q.d = v3_dot( U, U);
    q.e = -v3_dot( U, V);
    q.f = v3_dot( V, V);
    return q;
}

// if gradient of q is 0 in [0,1]x[0,1], return (m,n) where it is zero
// gradient of q is 2*( q->b + m*q->d + n*q->e, q->c + m*q->e + n*q->f)
// so must solve    ( q->d  q->e ) * (m) = -(q->b)
//          ( q->e  q->f )   (n)    (q->c)
static  int dq_zero( const quad2T* q, double* m, double* n)
{
double  det = q->d*q->f - q->e*q->e;
    if ( det <= 0.0)
    {   // note matrix be semi-positive definite, so negative determinant is rounding error
        return 0;
    }
    *m  = -( q->f*q->b - q->e*q->c)/det;
    *n  = -(-q->e*q->b + q->d*q->c)/det;

    return  0.0 <= *m && *m <= 1.0
    &&  0.0 <= *n && *n <= 1.0
    ;
}

// fill *n with minimising value, if any in [0,1], of n -> q(m0,n)
static  int m_edge_min( const quad2T* q, double m0, double* n)
{
double  r[3];   // coeffs of poly in n when m == m0
    r[0] = q->a + 2.0*m0*q->b + m0*m0*q->d;
    r[1] = q->c + m0*q->e;
    r[2] = q->f;
    return  ( quad_min( r, n)
        && *n > 0.0 && *n < 1.0
        );
}

// fill *m with minimising value, if any in [0,1], of m -> q(m,n0)
static  int n_edge_min( const quad2T* q, double* m, double n0)
{
double  r[3];   // coeffs of poly in m when n == n0
    r[0] = q->a + 2.0*n0*q->c + n0*n0*q->f;
    r[1] = q->b + n0*q->e;
    r[2] = q->d;
    return  ( quad_min( r, m)
        && *m > 0.0 && *m < 1.0
        );
}

// candidates for min, man
typedef struct
{   double  m,n;    // steps along lines
    double  d;  // distance squared between points
}   candT;

static  int find_cands( const quad2T* q, candT* c)
{
int nc = 0;
double  x, y;
    // the corners
    c[nc++] = (candT){ 0.0,0.0, eval_quad2( q, 0.0, 0.0)};
    c[nc++] = (candT){ 0.0,1.0, eval_quad2( q, 0.0, 1.0)};
    c[nc++] = (candT){ 1.0,1.0, eval_quad2( q, 1.0, 1.0)};
    c[nc++] = (candT){ 1.0,0.0, eval_quad2( q, 1.0, 0.0)};
    // the edges
    if ( m_edge_min( q, 0.0, &x))
    {   c[nc++] = (candT){ 0.0,x, eval_quad2( q, 0.0, x)};
    }
    if ( m_edge_min( q, 1.0, &x))
    {   c[nc++] = (candT){ 1.0,x, eval_quad2( q, 1.0, x)};
    }
    if ( n_edge_min( q, &x, 0.0))
    {   c[nc++] = (candT){ x, 0.0, eval_quad2( q, x, 0.0)};
    }
    if ( n_edge_min( q, &x, 1.0))
    {   c[nc++] = (candT){ x, 1.0, eval_quad2( q, x, 1.0)};
    }
    // where the derivatives are 0
    if ( dq_zero( q, &x, &y))
    {   c[nc++] = (candT){ x, y, eval_quad2( q, x, y)};
    }
    return nc;
}

// fill in r so that
// r[0] + 2*r[1]*z + r[2]*z*z = q( minm+z*(maxm-minm), minn+x*(maxn-minn))
static  void    form_quad
( const quad2T* q
, double minm, double maxm
, double minn, double maxn
, double* r
)
{
double  a = minm;
double  c = maxm-minm;
double  b = minn;
double  d = maxn-minn;
    r[0] =  q->a + 2.0*q->b*a + 2.0*q->c*b + q->d*a*a + 2.0*q->e*a*b + q->f*b*b;
    r[1] =  q->b*c + q->c*d + q->d*a*c + q->e*(a*d+b*c) + q->f*b*d;
    r[2] =  q->d*c*c + 2.0*q->e*c*d + q->f*d*d;
}

static  int find_points
( double* P, double* Q, double* R, double* S, double dsq, double* X, double* Y
)
{
double  m, n;
quad2T  q = set_quad2( P, Q, R, S);
candT   c[9];
int nc = find_cands( &q, c);    // find candidates for max and min
    // find indices of max and min
int imin = 0;
int imax = 0;
    for( int i=1; i<nc; ++i)
    {   if ( c[i].d < c[imin].d)
        {   imin = i;
        }
        else if ( c[i].d > c[imax].d)
        {   imax = i;
        }
    }
    // check if solution is possible -- should allow some slack here!
    if ( c[imax].d < dsq || c[imin].d > dsq)
    {   return 0;
    }
    // find solution 
double  r[3];
    form_quad( &q, c[imin].m, c[imax].m, c[imin].n, c[imax].n, r);
double  z;
    if ( solve_quad( r, dsq, &z))
    {   // fill in distances along
        m = c[imin].m + z*(c[imax].m - c[imin].m);
        n = c[imin].n + z*(c[imax].n - c[imin].n);
        // compute points
        for( int i=0; i<3; ++i)
        {   X[i] = P[i] + m*(Q[i]-P[i]);
            Y[i] = R[i] + n*(S[i]-R[i]);
        }
        return 1;
    }
    return 0;
}

1
投票

假设2个端点线A的从上线B我会用蛮力方法最接近的端点不同的距离。我会选择线A的中心点为线C的一端,并且在“插入距离”的步骤滑动线C对B线的另一端,直到我内“插入距离”距离“d”的。

如果最接近我来到“d”太大我会成立C线的新端点上的一个是半路B / T A的中心点和A线的端点最接近到最近的端点上线B.如果最接近我来“d”太小,我会提出新的端点中途的b / T A的中心点和A线的最远端点b线最接近的端点

重复这个过程中“插入步骤”迭代并返回给我的最近距离终点为“d”,如果没有找到一个可接受值达到最大迭代次数之前。然后,你可以判断,如果你的算法是不允许足够步骤或具有贴近“d”过于严苛的价值。

如果线A的2个端点为从上线B的最接近端点相同距离,然后用线B的最远端点如果这些都是相同的,它是在哪个方向上的初始步骤发生任意的。

此外,而不是简单地滑动第二端点B线,您可以使用跳跃到小中点相同的算法(在正确的方向),以节省计算数量。


1
投票

下面是一个迭代算法,需要一些数学而不是数学优化的一个深刻的理解。它的强大,但也许不是特别快。

在高层次上,这种算法是像二进制搜索(从技术上讲,三元搜索)。在每一对迭代,我们砍掉了什么仍然是各段,照顾如果存在保持一个有效的解决方案的恒定比例。我们可以用数学证明,在极限的迭代次数的增加,这两个领域缩小到分,如果存在的话,这些点是一个有效的解决方案。在实践中,我们在一些数量的迭代停止(例如,一百个,或当所述段是足够短),并在各段返回的任意点。

该算法采用两个数学成分。首先是计算一个点和线段之间的距离的公式。第二个问题是,正如我们沿一个分段扫描一个点,给其他的距离减小,然后增加的事实。

如果我有时间,我会展开说明。

from __future__ import division


def squared_distance_between_points(p, q):
    """Returns the squared distance between the point p and the point q."""
    px, py, pz = p
    qx, qy, qz = q
    return (px - qx)**2 + (py - qy)**2 + (pz - qz)**2


def squared_distance_between_point_and_segment(p, q, r):
    """Returns the squared distance between the point p and the segment qr."""
    px, py, pz = p
    qx, qy, qz = q
    rx, ry, rz = r
    # Translate the points to move q to the origin (p' = p - q and r' = r - q).
    px -= qx
    py -= qy
    pz -= qz
    rx -= qx
    ry -= qy
    rz -= qz
    # Project p' onto the line 0r'.
    # The point on this line closest to p' is cr'.
    c = (px * rx + py * ry + pz * rz) / (rx * rx + ry * ry + rz * rz)
    # Derive c' by clamping c. The point on the segment 0r' closest to p is c'r'.
    c = min(max(c, 0), 1)
    # Compute the distance between p' and c'r'.
    return squared_distance_between_points((px, py, pz),
                                           (c * rx, c * ry, c * rz))


def trisect(p, q):
    """Returns the point one-third of the way from the point p to the point q."""
    px, py, pz = p
    qx, qy, qz = q
    return ((2 * px + qx) / 3, (2 * py + qy) / 3, (2 * pz + qz) / 3)


def find_points_on_segments_at_distance(p, r, s, u, d, iterations=100):
    """Returns a point q on the segment pr and a point t on the segment su
       such that the distance between q and t is approximately d.
       If this is not possible (or barely possible), returns None."""
    d **= 2
    feasible = False
    for i in range(2 * int(iterations)):
        q1 = trisect(p, r)
        d1 = squared_distance_between_point_and_segment(q1, s, u)
        q2 = trisect(r, p)
        d2 = squared_distance_between_point_and_segment(q2, s, u)
        if d <= min(d1, d2):
            # Use convexity to cut off one third of the search space.
            if d1 <= d2:
                r = q2
            else:
                p = q1
        elif d <= max(d1, d2):
            # There is certainly a solution in the middle third.
            feasible = True
            p = q1
            r = q2
        elif (d <= squared_distance_between_points(p, s)
              or d <= squared_distance_between_points(p, u)):
            # There is certainly a solution in the first third.
            feasible = True
            r = q1
        elif (d <= squared_distance_between_points(r, s)
              or d <= squared_distance_between_points(r, u)):
            # There is certainly a solution in the last third.
            feasible = True
            p = q2
        else:
            # Definitely infeasible.
            return None
        # Swap the segments.
        p, r, s, u = s, u, p, r
    if not feasible:
        return None
    return p, r

1
投票

这可以通过使用只是解决一个二次多项式初等代数来解决。请看下面的推导:

给定的行段P通过点P1和P2,并通过点Q1,Q2我们可以定义射线P(t)的所定义的线段Q中定义:

P(T)= P1 + T V

其中T是正标量和V是单位矢量:

V =(P2 - P1)/ | P2 - P1 |

和线段Q(吨)为:

Q(T)= Q1 +吨(Q2 - Q1)

其中,t是在区间[0 1]的正标量。

在线路Q(t)输出到线P任何点的最短距离是由点的投影上线P上的投影是沿着线P的法线向量给定

             Q(t)
               |
               |
P1 ------------x------------ P2

因此,我们正在寻找在线路点x P,使得该向量(x - Q(t))的长度等于d:

| X - Q(t)的| ^ 2 = d ^ 2

点x可以使用射线P(t)的自T =(Q(T) - P1)来计算•五:

X = P((Q(T) - P1)•V)

X = P1 +((Q(T) - P1)•V)V

用P1 = - (P1•V)+ B(K(t)的•C)在

用P1 = - (P1•B)B +(K1•B)+ B M((K2 - K1)•B)在

凡(•)为点积。

X = C + T d

哪里

C = P1 - (P1•B)B +(K1•B)在

d =((Q2 - Q1)•V)w ^

现在方程如下:

| C + T d - Q1 - 吨(Q2 - Q1)| ^ 2 = d ^ 2

| C - Q1 + T(d - Q 2 + Q 1)| ^ 2 = d ^ 2

分组方式:

| T A + B | ^ 2 = d ^ 2

哪里

A =(d - Q2 + Q1)

B = C - Q1

以我们有方形:

(T A + B)•(T A + B)= d ^ 2

吨^ 2(A•A)+ 2 T(A•B)+(B•B - d ^ 2)= 0

这是一个简单的二次多项式。求解难道我们可以得到两个值,如果两个都是复数那么有没有真正的答案。如果两个都是真实的,然后我们有两个答案可能是由于对称性,我们必须选择一种吨区间[0 1]。

一旦我们有了吨我们可以计算使用Q(t)和在管线中的对应点X p在线段Q中的点

X = P((Q(T) - P1)•V)

如果参数(Q(T) - P1)•V是在区间[0 L],其中L是矢量的长度(P2 - P1),则x位于段线P的端部内,否则x是外然后没有答案已经找到。

© www.soinside.com 2019 - 2024. All rights reserved.