Vector 如何知道两条线段是否接近共线

Vector 如何知道两条线段是否接近共线,vector,parallel-processing,dot-product,cross-product,Vector,Parallel Processing,Dot Product,Cross Product,由于浮点精度的原因,我在确定两条线段是否共线时遇到了一些困难。如何确定线段是否与某些公差共线?编辑: 如果线段包含两个相同的点,则它们是共线的。如果它们共用一个点且接近平行,则它们接近共线 如果向量之间的角度小于您设定的阈值,则向量实际上是平行的。可能小于0.000027度,小数点相当于十分之一秒(这是纬度距离,相当于赤道的纵向距离,相差约10英尺;这与民用GPS的精度有关) 你没有告诉我们你在用什么语言或图书馆;在.NET的System.Windows.Media.3D库中,有一个Vector

由于浮点精度的原因,我在确定两条线段是否共线时遇到了一些困难。如何确定线段是否与某些公差共线?

编辑:

如果线段包含两个相同的点,则它们是共线的。如果它们共用一个点且接近平行,则它们接近共线

如果向量之间的角度小于您设定的阈值,则向量实际上是平行的。可能小于0.000027度,小数点相当于十分之一秒(这是纬度距离,相当于赤道的纵向距离,相差约10英尺;这与民用GPS的精度有关)

你没有告诉我们你在用什么语言或图书馆;在.NET的System.Windows.Media.3D库中,有一个Vector3D结构,它有一个AngleBeween()方法,使此检查成为一行

“基本”数学(实际上是向量三角,不是大多数定义的“基本”概念)是θ=cos-1(a*B/| a | B |);也就是说,两个向量的标量积的数量除以它们的大小积的弧余弦

向量A和向量B的点积(均包含X、Y和Z分量)为XAXB+YAYB+ZAZB。向量a的大小是sqrt(XA2+YA2+ZA2)

因此,在伪C-ish中:

//Vector is a simple immutable class or struct containing integer X, Y and Z components
public bool CloseEnough(Vector a, Vector b, decimal threshold = 0.000027m)
{
   int dotProduct = a.X*b.X + a.Y*b.Y + a.Z*b.Z;
   decimal magA = sqrt(a.X*a.X + a.Y*a.Y + a.Z*a.Z); //sub your own sqrt
   decimal magB = sqrt(b.X*b.X + b.Y*b.Y + b.Z*b.Z); //sub your own sqrt

   decimal angle = acos(dotProduct/(magA*magB)); //sub your own arc-cosine

   if(angle <= threshold
}
//Vector是一个简单的不可变类或结构,包含整数X、Y和Z分量
公共边界足够近(向量a,向量b,十进制阈值=0.000027m)
{
int dotProduct=a.X*b.X+a.Y*b.Y+a.Z*b.Z;
十进制magA=sqrt(a.X*a.X+a.Y*a.Y+a.Z*a.Z);//将您自己的sqrt
十进制magB=sqrt(b.X*b.X+b.Y*b.Y+b.Z*b.Z);//使用自己的sqrt
十进制角度=acos(点积/(磁伽*磁伽));//细分你自己的弧余弦

如果(角度我需要知道我的应用程序中的两个线段是否接近共线。这是关于从激光扫描中提取直线的。我将解释我使用的解决方案。它非常有效。(请原谅我的英语!)

我认为基思提出的接近共线的条件是错误的

如果它们共用一个点且接近平行,则它们接近共线

如果两段(或线)平行,但它们接近,我们可以认为它们接近共线。

我的解决方案是使用线的极坐标表示法

y*sin(θ)=ρ-x*cos(θ)


有了这种表示法,一条直线可以用θ和ρ表示成一个点。诀窍是,如果这些“点”很近,直线就很近共线。你只需要计算欧几里德距离,并使用一个阈值来确定它们是否近共线。

根据你的定义,有几种解决方案似乎是可行的“几乎共线”

通过4个点拟合一条直线 两条线段由4个点定义,可以通过两条线段的起点和终点拟合一条直线

您可以使用SVD通过4个点获得直线的最小二乘拟合,类似于此答案:

使用此方法,可以考虑线段的方向以及第一条线段的端点与第二条线段的起点之间的偏移

但是,如果两条线段平行但不共线,并且它们相对于它们之间的距离较短,则拟合线将垂直于两条线段。这对于单元测试没有问题,在单元测试中,您喜欢“几乎共线到零后的几位数字”,但对于其他应用程序,这可能是一个问题

独立使用平均方向和平均位置 或者,您可以使用两条线段的平均方向作为目标线的方向来构造目标线。Thuis可以通过
(v1/| v1 |+v2/| v2 |)计算/2
,如果向量
v1
v2
指向同一方向。然后使用4个点的平均值作为目标线的定位点

最后,您可以计算到目标线的4个点中的单个点,并将它们用作共线的度量

分段长度的影响&分段之间的间距 当你找到一个合适的“几乎共线”的定义时,你需要考虑,不同的线段长度如何影响结果,正如瓦希德在评论中指出的。此外,你应该考虑这两个线段之间的空间的影响。


通过计算4个点到目标线的距离,我试图减少不同线段长度的影响(每个线段仅贡献2个点),但我无法完全消除它

我认为,如果两条线近似共线,则应满足以下条件:

  • 两条直线的角度小于阈值
  • 点和两条线段之间的距离小于阈值
  • 我的解决方案:

    import math
    import numpy as np
    
    def line_to_angle(line, use_abs=True):
        x1, y1, x2, y2 = line[:4]
        if abs(x2 - x1) <= 0.0000001:
            angle_val = 90 if y2 > y1 else -90
        else:
            angle_val = np.degrees(math.atan((y2 - y1) / (x2 - x1)))
        if use_abs:
            angle_val = abs(angle_val)
        return angle_val
    
    
    def distance_line_point(p, line):
        p1 = np.array(p)
        p2, p3 = np.array(line[:2]), np.array(line[2:4])
        return np.abs(np.cross(p2-p1, p1-p3) / np.linalg.norm(p2-p1))
    
    
    def near_collinear(linea, lineb, angle_thres=1.5, distance_thres=5):
        pointa = ((linea[0] + lineb[0]) / 2, (linea[1] + lineb[1]) / 2)
        pointb = ((linea[2] + lineb[2]) / 2, (linea[3] + lineb[3]) / 2)
        mid_angle = line_to_angle([*pointa, *pointb], use_abs=False)
        angle_a, angle_b = line_to_angle(linea, use_abs=False), line_to_angle(lineb, use_abs=False)
        angle_diff_a, angle_diff_b = abs(angle_a - mid_angle), abs(angle_b - mid_angle)
        angle_diff_a = min(angle_diff_a, 180 - angle_diff_a)
        angle_diff_b = min(angle_diff_b, 180 - angle_diff_b)
        if angle_diff_a > angle_thres or angle_diff_b > angle_thres:
            return False
        mid_point = ((pointa[0] + pointb[0]) / 2, (pointa[1] + pointb[1]) / 2)
        distance_a = distance_line_point(mid_point, linea)
        distance_b = distance_line_point(mid_point, lineb)
        if distance_a > distance_thres or distance_b > distance_thres:
            return False
        return True
    
    导入数学
    将numpy作为np导入
    def管路到管路的角度(管路,使用abs=真):
    x1,y1,x2,y2=线[:4]
    如果abs(x2-x1)y1,则为-90
    其他:
    角度=np度(数学坐标((y2-y1)/(x2-x1)))
    如果使用防抱死制动系统:
    角度=绝对值(角度)
    返回角
    def距离线点(p,线):
    p1=np.数组(p)
    p2,p3=np.数组(第[:2]行),np.数组(第[2:4]行)
    返回np.abs(np.cross(p2-p1,p1-p3)/np.linalg.norm(p2-p1))
    def近共线(直线A,直线B,角度=1.5,距离=5):
    pointa=((linea[0]+lineb[0])/2,(linea[1]+lineb[1])/2)
    点B=((linea[2]+lineb[2])/2,(linea[3]+lineb[3])/2)
    中间角度=直线到角度([*点A,*点B],使用\u abs=False)
    角度a,角度b=直线到角度(直线a,使用绝对值=假),直线到角度(直线b,使用绝对值=假)
    角度差a,角度差b=abs(角度差a-中间角),abs(角度差b-中间角)
    角度差a=最小值(角度差a,180-角度差a)
    角度差b=最小值(a