【问题标题】:How to pick up line segments which is inside or intersect a circle?如何拾取在圆内或与圆相交的线段?
【发布时间】:2014-12-30 19:03:22
【问题描述】:

我有一组线段AB1, AB2, ... ABn。每个都有(Ax, Ay), (Bx, By) 坐标。然后,我有一个中心坐标(Cx,Cy)和 r(半径)的圆。

问题:我如何检测哪条线段位于圆上(如图)? .

我尝试用 Python 表达我的想法:

import numpy as np
import pylab as plt

def detect(A,B, C, r):
    '''
    Returns 'True' if line is inside or intersected the circle, otherwise 'False'.
    Inputs:
       - A - segment line coordinate (Ax, Ay)
       - B - segment line coordinate (Bx, By)
       - C - circle coordinate (Cx, Cy)
       - r - circle radius
    ''' 
    # Do process for detection
    return (boolean)

def plot_detected(An, Bn, C, r):
    '''
    Plots newly detected line segments with red color 
    while the rest remains with blue color 
    '''
    plt.figure(1)
    plt.subplot(111)
    for A, B in zip(An, Bn):
        if detect(A, B, C, r):
              line1, = plt.plot([ A[0], B[0] ], [ A[1], B[1] ], 'ro-')
        else:
              line2, = plt.plot([ A[0], B[0] ], [ A[1], B[1] ], 'bo-')
    pl.legend([line1, line2], ('detected','un-detected'))
    plt.show()

def main():
    C = [18.5, 18.5]
    r = 2.4
    Ax = np.array([16.2, 17.2, 22.2, 18.2, 23.8, 18.8])
    Ay = np.array([22.8, 20.6, 23.8, 18.4, 20.8, 22.8])
    Bx = np.array([21.8, 19.8, 18.2, 19.8, 17.2, 22.8])
    By = np.array([17.8, 17.2, 19.2, 19.2, 16.8, 20.8])
    An = np.vstack([Ax, Ay]).T
    Bn = np.vstack([Bx, By]).T

    plot_detected(An, Bn, C, r)

if __name__ == '__main__':
    main()

提前感谢您的帮助。

【问题讨论】:

  • 你只是想找到圆中的线段部分吗?或者试图找出哪些线段与圆相交?
  • @Maria 。我正在尝试找出哪些线段不仅相交而且位于圆内。

标签: python numpy intersection clipping line-intersection


【解决方案1】:

对于每条线,您应该能够计算线上距离圆心最小的点。为此,您将中心的位置向量投影到线的方向向量上。称其为最小距离点 P。如果 P 在圆内(即其坐标的平方和的 sqrt 小于圆半径)并且 P 也在线段的端点之间,则线段与圈子。

您还必须检查线端点本身是否在圆内。

def detect( A, B, C, r ):

    # First, let's express each vector as a complex number.
    # This simplifies the rest of the code because we can then subtract them
    # from each other in one statement, or find their length with one statement.
    # (Downside: it does not allow us to generalize the code to spheres in 3D.)
    OA = complex( *A )
    OB = complex( *B )
    OC = complex( *C )

    # Now let's translate into a coordinate system where A is the origin
    AB = OB - OA
    AC = OC - OA

    # Before we go further let's cover one special case:  if either A or B is actually in
    # the circle,  then mark it as a detection
    BC = OC - OB
    if abs( BC ) < r or abs( AC ) < r: return True

    # Project C onto the line to find P, the point on the line that is closest to the circle centre
    AB_normalized = AB / abs( AB )
    AP_distance = AC.real * AB_normalized.real  +  AC.imag * AB_normalized.imag    # dot product (scalar result)
    AP = AP_distance * AB_normalized   # actual position of P relative to A (vector result)

    # If AB intersects the circle, and neither A nor B itself is in the circle,
    # then P, the point on the extended line that is closest to the circle centre, must be...

    # (1) ...within the segment AB:
    AP_proportion = AP_distance / abs( AB )   # scalar value: how far along AB is P?
    in_segment =   0 <= AP_proportion <= 1

    # ...and (2) within the circle:
    CP = AP - AC
    in_circle = abs( CP ) < r

    detected = in_circle and in_segment


    #OP = OA + AP
    #plt.plot( [OC.real, OP.real], [OC.imag, OP.imag], {True:'rs--', False:'bs--'}[detected] )

    return detected



def plot_detected(An, Bn, C, r):
    '''
    Plots newly detected line segments with red color 
    while the rest remains with blue color 
    '''
    plt.figure(1)
    plt.clf()
    plt.subplot(111)
    for A, B in zip(An, Bn):
        if detect(A, B, C, r):
              line1, = plt.plot([ A[0], B[0] ], [ A[1], B[1] ], 'ro-')
        else:
              line2, = plt.plot([ A[0], B[0] ], [ A[1], B[1] ], 'bo-')
    plt.legend([line1, line2], ('detected','un-detected'))
    circle = mpatches.Circle( C, r, fc="none", ec='k' )
    plt.gca().add_patch(circle)
    plt.gca().set_aspect('equal')

【讨论】:

  • +1 表示算法,但我认为如果您可以填写函数 detect 会很有用,因为这里的许多人无法进行数学运算、找到正确的公式等。
  • @jez 。你能通过填写函数detect来解释你的想法吗?我离数学有点远。对不起。
  • 我会在我有时间的时候发布一个解决方案的草图,这应该是在几个小时内,假设没有其他人超过我。同时,我建议你像拼图一样攻击它——即将它分解。我很确定你会得到一个短语,比如“坐标平方和的平方根”。如果“将中心的向量投影到线的向量上”没有意义,那么谷歌“什么是向量投影”或“我如何将一个向量投影到另一个”或“我如何找到一条线上的点离另一个点最近”。这个过程是我知道这些东西的唯一原因。
  • @Jez 。我只是改变了这个数字,以便更好地了解我想要做什么。这些线基于我的代码中给出的坐标。
【解决方案2】:

1st:消除任务定义的拓扑结构

术语定义:

所有“线段”都已给出。

每个线段都由笛卡尔的[x,y] 对定义,用于其每个端点。

关于拓扑条件的初始问题陈述表明:

检测哪个线段 位于圆(图中)或不是?

作者自己的评论补充:

找出哪些线段不仅相交而且位于圆内。

问题标题指出:

选取内部相交

线段

从标题和评论看来,为了满足“线段”到“躺在一个圆圈”的拓扑条件,它是够了,如果它的任何端点{ 1 | 2 }圈内。

如果此拓扑假设失败,则 jez 发布的矢量法线距离解是正确的。

解决方案:

基于明确的术语定义和拓扑条件,满足定义约束的“线段”-s的集合通过以下方式获得:

def detect( A, B, C, r ):
    '''
    Returns 'True' if line is inside or intersected the circle, otherwise 'False'.
    Inputs:
       - A - segment line coordinate (Ax, Ay)
       - B - segment line coordinate (Bx, By)
       - C - circle coordinate (Cx, Cy)
       - r - circle radius
    '''
    # Do process for detection

    aBooleanANSWER = ( aCartesianDISTANCE( A, C ) <= r )
                     or
                     ( aCartesianDISTANCE( B, C ) <= r )

    return ( aBooleanANSWER )

【讨论】:

  • 简单漂亮的答案。
  • 您的图中有一条红线未检测到。它在外面开始和结束,但穿过圆圈。
  • @user3666197 。您确定您的代码给出了问题中显示的相同数字吗?恐怕您的代码没有为此进行优化。您可以通过绘图来验证它
  • 错误答案,当点在圆外但线与圆相交时不检测大小写。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-03
  • 1970-01-01
  • 2016-05-11
相关资源
最近更新 更多