[英]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.