给定两条线段,找出线段之间距离为d的两点。
这类似于“两个线段之间的最短距离问题”,只是我们要解决线段上由给定距离d分隔的两点。
每条线段由两个三维点组成。
我通过谷歌搜索找到的数学既让我害怕又让我困惑。我是个程序员,但我很难理解解决这样一个问题背后的证据和分析。
输入:2个线段和距离d
输出:每个段上的2个点是彼此距离D,或者如果没有两点存在,则没有。

最佳答案

这是一个非迭代解我担心数学可能会使你生气,尽管这里没有什么复杂的东西。
首先,最简单的方法是将距离平方。
一条三线由点P和Q来描述,另一条由点R和S来描述,然后一种说明问题的方法是,我们要找到标量m和n,这样一个点沿着第一条线的分数m和一个点沿着第二条线的分数n之间的距离的平方就是给定的dsq。
我们必须将m和n限制在0和1之间(包括0和1),以便我们的点真正位于线段上。
如果有m和n,那么点是

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

假设我们首先找到DSQ的最小值和最大值。这将告诉我们是否有一个解决方案:如果给定的DSQ值小于最小值或大于最大值,则没有解决方案,并且我们可以停止。
让最小值出现的点的m和n值是Mymin和nnmin,最大值是MyMax和nax Max。如果我们引入一个新的变量z(in [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的。所以当z从0增加到1时,Dsq的值必须通过我们想要的值也就是说,我们只需要搜索使Dsq成为我们想要的值的z值。
使问题(相对地)变得简单的是x和y之间的距离平方是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))或沿某个边缘(AT(0,N)或(1,N)的某个N,或(M,0)或(M,1)为某个M)或在DSQ(相对于M和N的导数均为0)的中间点。
例如,在边上说(0,n),我们得到Dsq的二次n,所以很容易找到最大值。
此外,当我们来看最小值和最大值之间的“线”时,如果我们将m(z)和n(z)代入到Dsq的公式中,则在较繁琐的代数之后,得到Z中的二次方,因此很容易找到给出DSQ期望值的Z的值。
好吧,这篇文章已经很长了,下面是实现这些想法的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;
}

10-06 13:54