【问题标题】:Find if segment comes within distance of another one查找段是否在另一个段的距离内
【发布时间】:2021-03-21 21:08:45
【问题描述】:

我有一堆线段(我拥有的数据是组成线段 [x1,y1] 和 [x2,y2] 的 2 个点)并且想根据它们的位置对它们进行分类。如果一个片段与另一个片段足够接近,那么我想将它们放在一起。如果非要用一句话来形容的话:我想找到距离段任意点5px的所有相邻段。

与绘制的图片类似的规则:

我正在研究不同的算法,但其中大多数都处理交叉点并预测线是否会相交。这对我来说真的不起作用,因为我不想将线条延续到无穷大,我只想知道它们是否彼此相距 5px。

有谁知道我可以如何解决这个问题(并且相对较快)?您知道任何可以提供帮助的框架吗? (我正在查看最近的邻居,但我找不到任何处理几何而不是点的框架。

谢谢!

【问题讨论】:

    标签: python algorithm geometry categorization


    【解决方案1】:

    (我已经修改了我之前的答案。那个答案有一些缺点。我认为我的新答案显示了一个更简单、更强大的解决方案。)

    您有两个线段,S 带有点 S0 和 S1,T 带有点 T0 和 T1。当这些线段在某一点的距离小于 r 时,就会检测到碰撞。

    对于线段S,得到方向向量Δs、线段长度s和归一化方向向量u。 p>

        Δs = S1 - S0
    s = |Δs|
    u = Δs / s

    单位向量u和点S0可以描述任意点P到点P'的变换:

        P′x =   (Px - S0x) · ux + (Py - S0y) · uy
    P′y = - (Px - S0x) · uy + (Py - S0y) · ux

    在这个变换中,段 S 的点是:

        S′0 = (0, 0)
    S′1 = (s, 0)

    对于变换后的点T′0和T′1,y分量可以解释为到S的有符号距离。现在可以进行几个测试:

    • 第一个测试是T′0或T′1是否在段S的r距离内或半径内S0′或S1′的r。如果是这样,我们就成功了。

    • 接下来的测试是两条线是否相交。只有当 T′0y 或 T′1y 的符号不同时,才会发生这种情况。如果是这样,我们就成功了。

    • 对于最后一个测试,我们通过在 T 与 x 轴对齐的系统中将 S 转换为 S'' 来反转第一个测试。然后测试变换点S′′0或S′′1之一是否在T′′的r内。如果是这样,我们就成功了,否则就失败了。

    Python 代码如下。我还更新了我的JS Fiddle

    注意事项:

    • 我的旧答案中的纵向变量 a 和距离 d 实际上与此处的 x' 和 y' 相同。我认为转换更简单。

    • 此解决方案仅测试 (1) T 的点是否在距 S r 的距离内,(2) 线是否相交以及 (3) S 的点是否在 a r 到 T 的距离。共线线段的情况被测试(1)和(3)捕获。

    • 下面的代码不处理零长度段(S0 = S1 或 T0 = T1) 显式地返回一个非空向量作为空向量的范数似乎可以解决问题 - 测试 (1) 和 (3) 捕获这些情况。

    Python 代码:

    import math
    
    class Point:
        """ A point P(x, y) in 2D space
        """
    
        def __init__(self, x, y):
            self.x = float(x)
            self.y = float(y)
    
    class Vector:
        """ A vector v(x, y) in 2D space
        """
    
        def __init__(self, x, y):
            self.x = x
            self.y = y
        
        def mag(self):
            """ magnitude of the vector
            """
            
            return math.hypot(self.x, self.y)
        
        def norm(self):
            """ return the normalized vector or (0, 0)
            """
        
            a = self.mag()
            
            if a*a < 1.0e-16:
                return Vector(1, 0)
            
            return Vector(self.x / a, self.y / a)
        
    
    
    def diff(p, q):
        """ difference vector (q - p)
        """
    
        return Vector(q.x - p.x, q.y - p.y)
    
    def within(p, dx, r):
        """ Is p within r of point (dx, 0)?
        """
    
        x = p.x - dx
        y = p.y
        
        return x*x + y*y <= r*r
    
    def rot(p, u):
        """ Rotate point p to a coordinate system aligned with u.
        """
    
        return Point(p.x * u.x + p.y * u.y,
                    -p.x * u.y + p.y * u.x)
    
    def collision(s, t, r):
        """ Do the line segments s and t collide with a radius r
        """
    
        ds = diff(s[0], s[1])
        ss = ds.mag()    
        u = ds.norm()
        
        a0 = rot(diff(s[0], t[0]), u)
        a1 = rot(diff(s[0], t[1]), u)
    
        # Test T0 and T1 against S
        
        if -r <= a0.y <= r and -r <= a0.x <= ss + r:
            if a0.x < 0: return within(a0, 0, r)
            if a0.x > ss: return within(a0, ss, r)
            return True    
        
        if -r <= a1.y <= r and -r <= a1.x <= ss + r:
            if a1.x < 0: return within(a1, 0, r)
            if a1.x > ss: return within(a1, ss, r)
            return True
    
        # Test intersection
        
        if a0.y * a1.y < -0.9 * r * r:
            a = -a0.y * (a1.x - a0.x) / (a1.y - a0.y) + a0.x
            
            if 0 <= a <= ss: return True
    
        # Test S0 and S1 against T
        
        dt = diff(t[0], t[1])
        tt = dt.mag()    
        v = dt.norm()
        
        b0 = rot(diff(t[0], s[0]), v)
        b1 = rot(diff(t[0], s[1]), v)
    
        if 0 <= b0.x <= tt and -r <= b0.y <= r: return True
        if 0 <= b1.x <= tt and -r <= b1.y <= r: return True
           
        return False
    

    【讨论】:

    • 您好,感谢您的回答。这是非常有见地和乐于助人的。我正在玩你的 JS Fiddle,我发现了一个你的代码没有涵盖的情况:当段头并排但头没有悬停在每个段的中间时。例如,如果您将线条并排放置(在延续中),类似于“.---. .---。”在哪里 ”。”是段头,“-”是段体。你会如何处理这个案子?再次感谢!
    • 没关系,我意识到处理头部很容易。非常感谢您的帮助!
    • 嗯。当线条共线时也会出现问题,可能是因为我没有发现 d0 = d1 的情况。我没有检查提示周围的圆圈,因为我认为这种情况会被反向检查捕获,但您的示例不是。
    • 我已经用 Python 实现了您的解决方案,并用我的数据集对其进行了测试。计算apos和aneg时似乎有除以0。段坐标为:S([2834, 813], [2844, 813]) 和 T([2843, 809], [2855, 809])。所以看起来这种共线性对我来说确实是一个问题。你会如何解决这个问题?
    • 是的,当 d₀ = d₁ 时有问题。我认为您可以在计算它们之后并在计算 apos 和 aneg 之前捕捉到这一点。如果 d₀ = d₁,则返回 true 如果 |d₀| s 的可能性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    • 2015-10-11
    相关资源
    最近更新 更多