计算分隔两条线的最小平移量

Calculating the minimum translation to seperate two lines

本文关键字:分隔 两条线 计算      更新时间:2023-10-16

给定R2中两个相交的线段(AB和CD),求作用于CD的平移量最小,使其不再与AB相交

我已经试过了。我计算每条线上每一点到另一条线上的距离。然后从4个值中选取最小的值并将其应用于直线的垂线。然而,我计算的翻译往往是错误的方向。我该如何解决这个问题?

#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <iterator>
class Vector2
{
public:
    double x;
    double y;
    Vector2() : x(x), y(y) {}
    Vector2(double x, double y) : x(x), y(y) {}
    Vector2 operator*(double val) { return Vector2(x*val, y*val);}
    Vector2 operator/(double val) { return Vector2(x/val, y/val);}
    Vector2 operator+(Vector2 &v) { return Vector2(x+v.x, y+v.y);}
    Vector2 operator-(Vector2 &v) { return Vector2(x-v.x, y-v.y);}
     Vector2 Perpendicular()  { return Vector2(y, -x); }
     double Dot( Vector2 &v)  {return (x * v.x) + (y * v.y); }
     double Magnitude()  { return std::sqrt(Dot(*this)); }
     Vector2 Normal()  { return *this / Magnitude(); }
     double GetDistance( Vector2 &v)  {  Vector2 d = *this - v; return d.Magnitude(); }
};

class Line
{
public:
    Line() : a(Vector2()), b(Vector2()) {}
    Line( Vector2 a,  Vector2 b) : a(a), b(b) {};
     double DistanceFromPoint( Vector2 &p) ;
     Vector2 GetTranslation( Line &l) ;
     Vector2& GetPoint(unsigned i) {if (i==0) return a; else return b;}
     double GetLength()  { return GetPoint(0).GetDistance(GetPoint(1)); }
    Vector2 a;
    Vector2 b;
};
 double Line::DistanceFromPoint( Vector2 &p) 
{
     double l2 = GetLength() * GetLength();
     Vector2 pv = p - GetPoint(0);
     Vector2 wv = GetPoint(1) - GetPoint(0);
     double t = std::max(0.d, std::min(1.d, pv.Dot(wv) / l2));
     Vector2 projection = (wv * t) + GetPoint(0);
    return p.GetDistance(projection);
}
 Vector2 Line::GetTranslation( Line &l) 
{
    // Calculate Distances from each point to rthe opposite line
    std::vector<double> dist(4);
    dist[0] = DistanceFromPoint(l.GetPoint(0));
    dist[1] = DistanceFromPoint(l.GetPoint(1));
    dist[2] = l.DistanceFromPoint(GetPoint(0));
    dist[3] = l.DistanceFromPoint(GetPoint(1));
    //Get the smallest distance
    auto it = std::min_element(std::begin(dist), std::end(dist));
    double min = *it;
    unsigned pos = std::distance(std::begin(dist), it);
    // Get the normalized perpendicular of line
    Vector2 axis;
    if (pos == 2 || pos == 3)
        axis = (GetPoint(1) - GetPoint(0)).Perpendicular().Normal();
    else
        axis = (l.GetPoint(1) - l.GetPoint(0)).Perpendicular().Normal();
    std::cout << "min: " << min << std::endl;
    std::cout << "axis: (" << axis.x << "," << axis.y << ")" << std::endl;
    //Apply that min to the perpendicular
    return axis * min;
}
int main()
{
    Line A;
    Line B;
    Vector2 t;
    std::cout << "Left" << std::endl;
    A = Line(Vector2(0, 4), Vector2(8, 4));
    B = Line(Vector2(2, 0), Vector2(2, 6));
    t = A.GetTranslation(B);
    std::cout << "Expected: (-2, 0)" << std::endl;
    std::cout << "Got: (" << t.x << "," << t.y << ")" << std::endl << std::endl;
    std::cout << "Right" << std::endl;
    B = Line(Vector2(6, 0), Vector2(6, 6));
    t = A.GetTranslation(B);
    std::cout << "Expected: (2, 0)" << std::endl;
    std::cout << "translation: (" << t.x << "," << t.y << ")" << std::endl << std::endl;
    std::cout << "Top" << std::endl;
    B = Line(Vector2(4, 0), Vector2(4, 6));
    t = A.GetTranslation(B);
    std::cout << "Expected: (0, -2)" << std::endl;
    std::cout << "translation: (" << t.x << "," << t.y << ")" << std::endl << std::endl;
    std::cout << "Bottom" << std::endl;
    B = Line(Vector2(4, 6), Vector2(4, 8));
    t = A.GetTranslation(B);
    std::cout << "Expected: (0, -2)" << std::endl;
    std::cout << "translation: (" << t.x << "," << t.y << ")" << std::endl;
}

你的想法会成功的。你只需要有一个像@Tommy提到的带符号距离函数。此外,请注意,您还必须使正确垂直方向,这取决于距离函数中的符号约定。

这是一个演示

您的方法是正确的。根据Minkowski和的推理,最短的分离向量将垂直于其中一条线,并且长度足以将其中一个端点推到另一条线的表面。

但是你的DistanceFromPoint返回一个无符号距离:它总是零或正。然后用一条垂线乘以它。所以你最终会有50%的时间走错路,因为你的距离没有符号。

你可能更喜欢重新排列,尤其是因为它可以节省一些平方根:

  1. 得到两个轴;
  2. 对于每条线,从该线上的任何点到另一条线的每个端点的向量投影到该线的轴上。
  3. 按量级选取最短的结果;
  4. 将分离轴计算为最短次数的负数乘以计算轴。

…因为如果你沿着q轴被n单元嵌入那么解决这个问题的方法就是沿着q移动-n单元;所以选择最小的n