簡體   English   中英

計算線線交點 C++ 的最常用方法?

[英]Most common way to compute line line intersection C++?

似乎沒有辦法使用boost::geometry計算線線交點,但我想知道在 C++ 中最常用的方法是什么?

我需要二維中兩條無限線的交集算法,如果它會更快,它可以是兩個不同的函數,例如:

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

PS我真的盡量避免輪子發明,所以傾向於使用一些庫。

我找到的找到線條交叉點的最佳算法是: Christer Ericson的實時碰撞檢測 ,這本書的副本可以在這里找到。

第146頁的第5章介紹了如何找到最接近3D線的點,這也是2D線的交叉點......示例代碼用C表示。

注意:注意平行線,它們可能導致除以零錯誤。

你可以嘗試我的代碼,我正在使用boost::geometry ,我在main函數中放了一個小測試用例。

我定義了一個具有兩個點作為屬性的類Line。

交叉產品是一種非常簡單的方法來了解兩條線是否相交。 在2D中,您可以計算perp點積(參見perp函數),它是2D平面法線向量上的叉積投影。 要計算它,您需要獲得每一行的方向向量(請參閱getVector方法)。

在2D中,您可以使用perp點積和線的參數方程得到兩條線的交點。 我在這里找到了解釋。

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;
}

編輯:關於交叉乘積和perp點積的更多細節+刪除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;

    }

以參數形式表示其中一行,以隱式形式表示另一行:

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|

對於小µ

也許常見的方法是近似無窮大? 從我的庫使用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);

然后你可以測試'lines'是否像這樣交叉:

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

extendSeg()只是按給定的數量在兩個方向上擴展段。


還要記住 - 為了支持無限線算術, 類型也應該支持無限值 然而,這里假設您正在尋找數值解!

為了解決這個問題,我拼湊了下面的函數,卻意外地發現它不能計算線段的交點,而是計算線段的交點。

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);
    }
};

那里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM