最常用的计算线-线交点的方法是C++

Most common way to compute line line intersection C++?

本文关键字:方法 C++ 常用 计算      更新时间:2023-10-16

似乎没有办法使用boost::geometry计算线与线的交点,但我想知道在C++中最常见的方法是什么?

我需要2D中两条无限长线的相交算法,如果它更快的话,它可以是两个不同的函数,比如:

bool line_intersection(line,line);
point line_intersetion(line,line);

附言:我真的尽量避免轮子的发明,所以倾向于使用一些图书馆。

我发现的寻找直线交点的最佳算法在:Christer Ericson的《实时碰撞检测》,这本书的副本可以在这里找到。

从第146页开始的第5章描述了如何找到3D线的最近点,也就是2D线的交叉点。。。C.中的示例代码

注意:注意平行线,它们可能会导致除以零的错误。

以参数形式表示其中一行,另一行以隐式形式表示:

X = X0 + t (X1 - X0), Y= Y0 + t (Y1 - Y0)
S(X, Y) = (X - X2) (Y3 - Y2) - (Y - Y2) (X3 - X2) = 0

通过线性关系,你就得到了

S(X, Y) = S(X0, Y0) + t (S(X1, Y1) - S(X0, Y0)) = S0 + t (S1 - S0) = 0

由此得到t,从t得到交点的坐标。

它总共需要15次加法、6次乘法和一次除法。

退化由S1 == S0表示,这意味着直线是平行的。在实践中,由于截断错误或其他原因,坐标可能不准确,因此等于0的测试可能失败。解决方法是考虑测试

|S0 - S1| <= µ |S0|

对于小CCD_ 5。

你可以试试我的代码,我使用的是boost::geometry,我在主函数中放了一个小的测试用例。

我定义了一个类Line,其中有两个点作为属性。

叉积是知道两条线是否相交的一种非常简单的方法。在2D中,可以计算perp点积(参见perp函数),它是叉积在2D平面法向量上的投影。要计算它,需要获得每条线的方向向量(请参见getVector方法)。

在二维中,可以利用两点积和直线的参数方程求出两条直线的交点。我在这里找到了一个解释。

intersect函数返回一个布尔值来检查两条线是否相交。如果它们相交,它将通过参照计算交点。

#include <cmath>
#include <iostream>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
namespace bg = boost::geometry;
// Define two types Point and Vector for a better understanding
// (even if they derive from the same class)
typedef bg::model::d2::point_xy<double> Point;
typedef bg::model::d2::point_xy<double> Vector;
// Class to define a line with two points
class Line
{
  public:
    Line(const Point& point1,const Point& point2): p1(point1), p2(point2) {}
    ~Line() {}
    // Extract a direction vector
    Vector getVector() const 
    {
      Vector v(p2);
      bg::subtract_point(v,p1);
      return v;
    }
    Point p1;
    Point p2;
};
// Compute the perp dot product of vectors v1 and v2
double perp(const Vector& v1, const Vector& v2)
{
  return bg::get<0>(v1)*bg::get<1>(v2)-bg::get<1>(v1)*bg::get<0>(v2);
}
// Check if lines l1 and l2 intersect
// Provide intersection point by reference if true
bool intersect(const Line& l1, const Line& l2, Point& inter)
{
  Vector v1 = l1.getVector();
  Vector v2 = l2.getVector();
  if(std::abs(perp(v1,v2))>0.)
  {
    // Use parametric equation of lines to find intersection point
    Line l(l1.p1,l2.p1);
    Vector v = l.getVector();
    double t = perp(v,v2)/perp(v1,v2);
    inter = v1;
    bg::multiply_value(inter,t);
    bg::add_point(inter,l.p1);
    return true;
  }
  else return false;
}
int main(int argc, char** argv)
{
  Point p1(0.,0.);
  Point p2(1.,0.);
  Point p3(0.,1.);
  Point p4(0.,2.);
  Line l1(p1,p2);
  Line l2(p3,p4);
  Point inter;
  if( intersect(l1,l2,inter) )
  {
    std::cout<<"Coordinates of intersection: "<<inter.x()<<" "<<inter.y()<<std::endl;
  }
  return 0;
}

编辑:关于交叉乘积和逐点乘积的更多详细信息+删除tol参数(主题外)

此代码应该适用于您。你也许可以对它进行一点优化:

template <class Tpoint>
    Tpoint line<Tpoint>::intersect(const line& other) const{
        Tpoint x = other.first - first;
        Tpoint d1 = second - first;
        Tpoint d2 = other.second - other.first;
        auto cross = d1.x*d2.y - d1.y*d2.x;
        auto t1 = (x.x * d2.y - x.y * d2.x) / static_cast<float>(cross);
        return first + d1 * t1;
    }

也许一种常见的方法是近似无穷大?使用boost::geometry:从我的库中

// prev and next are segments and RAY_LENGTH is a very large constant
// create 'lines'
auto prev_extended = extendSeg(prev, -RAY_LENGTH, RAY_LENGTH);
auto next_extended = extendSeg(next, -RAY_LENGTH, RAY_LENGTH);
// intersect!
Points_t isection_howmany;
bg::intersection(prev_extended, next_extended, isection_howmany);

然后你可以测试"线"是否像这样相交:

if (isection_howmany.empty())
    cout << "parallel";
else if (isection_howmany.size() == 2)
    cout << "collinear";

CCD_ 11在两个方向上简单地将该段延伸给定的量。


还要记住,要支持无限行算术,类型还应支持infinite值。然而,这里的假设是,你正在寻找一个数值解决方案!

为了解决这个问题,我拼凑了下面的函数,但意外地发现它不能计算线段的交点,而是计算直线的交点。

class Solution {
    typedef complex<double> point;
#define x real()
#define y imag()
    struct LinePara
    {
        double k;
        double b;
    };
    LinePara getLinePara(float x1, float y1, float x2, float y2)
    {
        LinePara ret;
        double m = x2 - x1;
        if (m == 0)
        {
            ret.k = 1000.0;
            ret.b = y1 - ret.k * x1;
        }
        else
        {
            ret.k = (y2 - y1) / (x2 - x1);
            ret.b = y1 - ret.k * x1;
        }
        return ret;
    }
    struct line {
        double a, b, c;
    };
    const double EPS = 1e-6;
    double det(double a, double b, double c, double d) {
        return a * d - b * c;
    }
    line convertLineParaToLine(LinePara s)
    {
        return line{ s.k,-1,s.b };
    }
    bool intersect(line m, line n, point& res) {
        double zn = det(m.a, m.b, n.a, n.b);
        if (abs(zn) < EPS)
            return false;
        res.real(-det(m.c, m.b, n.c, n.b) / zn);
        res.imag(-det(m.a, m.c, n.a, n.c) / zn);
        return true;
    }
    bool parallel(line m, line n) {
        return abs(det(m.a, m.b, n.a, n.b)) < EPS;
    }
    bool equivalent(line m, line n) {
        return abs(det(m.a, m.b, n.a, n.b)) < EPS
            && abs(det(m.a, m.c, n.a, n.c)) < EPS
            && abs(det(m.b, m.c, n.b, n.c)) < EPS;
    }
    vector<double> mian(vector<vector<double>> line1, vector<vector<double>> line2)
    {
        vector<point> points;
        points.push_back(point(line1[0][0], line1[0][1]));
        points.push_back(point(line1[1][0], line1[1][1]));
        points.push_back(point(line2[0][0], line2[0][1]));
        points.push_back(point(line2[1][0], line2[1][1]));
        line li1 = convertLineParaToLine(getLinePara(line1[0][0], line1[0][1], line1[1][0], line1[1][1]));
        line li2 = convertLineParaToLine(getLinePara(line2[0][0], line2[0][1], line2[1][0], line2[1][1]));
        point pos;
        if (intersect(li1, li2, pos))
        {
            return{ pos.x ,pos.y };
        }
        else
        {
            if (equivalent(li1, li2)) {
                if (points[1].x < points[2].x)
                {
                    return vector<double>{ points[1].x, points[1].y };
                }
                else if (points[1].x > points[2].x)
                {
                    return vector<double>{ points[2].x, points[2].y };
                }
                else if (points[1].x == points[2].x)
                {
                    if (points[1].y < points[2].y)
                    {
                        return vector<double>{ points[1].x, points[1].y };
                    }
                    else if (points[1].y > points[2].y)
                    {
                        return vector<double>{ points[2].x, points[2].y };
                    }
                }
                else
                {
                    return vector<double>{ points[2].x, points[2].y };
                }
            }
            else
            {
                return {}/* << "平行!"*/;
            }
            return {};
        }
    }
public:
    vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
        vector<vector<double>> line1{ {(double)start1[0],(double)start1[1]},{(double)end1[0],(double)end1[1] } };
        vector<vector<double>> line2{ {(double)start2[0],(double)start2[1]},{(double)end2[0],(double)end2[1] } };
        return mian(line1, line2);
    }
};

从那里