0

I have sets of line segments AB1, AB2, ... ABn. Each one has (Ax, Ay), (Bx, By) coordinates. Then, I have circle with a center coordinate (Cx, Cy) and r (radius).

Problem: How can I detect which line segment lies on a circle (in figure) OR not? description in figure.

I tried to formulate my idea in 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()

Thank you for your help in advance.

Spider
  • 967
  • 3
  • 14
  • 35
  • Are you just trying to find the part of the line segment that's in the circle? Or trying to find which segments intersect the circle? – Maria Zverina Nov 04 '14 at 02:36
  • @Maria . I am trying to find which segments not only intersect and also lies in the circle. – Spider Nov 04 '14 at 11:21

2 Answers2

2

For each line, you should be able to compute the point on the line that is minimally distant from the circle centre. To do that, you project the position vector of the centre onto the line's direction vector. Call that minimally-distant point P. If P is within the circle (i.e. the sqrt of the sum of squares of its coordinates is less than the circle radius) and P is also between the line segment's end points, then the line segment intersects the circle.

You also have to check whether the line endpoints themselves are inside the circle.

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')
jez
  • 14,867
  • 5
  • 37
  • 64
  • +1 for algorithm but I think it would be useful if you could fill out the function `detect` as many here won't be able to do the math, find the right formula, etc. – tom10 Nov 04 '14 at 11:24
  • @jez . Can you explain your idea by filling the function `detect` ? I am a bit far away from math. Sorry. – Spider Nov 04 '14 at 11:26
  • I'll post a sketch of a solution when I get time, which should be in a few hours, assuming nobody else beats me to it. Meanwhile, I suggest you attack it like a puzzle—i.e., break it down. I'm pretty sure you can get a phrase like "the square root of the sum of the squares of its coordinates". If "project the vector of the centre onto the vector of the line" makes no sense, then google "what is vector projection" or "how do I project one vector onto another" or "how do I find the point on a line that is nearest to another point". That process is the only reason I know this stuff. – jez Nov 04 '14 at 14:20
  • @Jez . I just changed the figure in order to get better idea what I am trying to do. These lines are based on coordinates given in my code. – Spider Nov 04 '14 at 15:09
0

1st: Disambiguate topology of the Task Definition

Definition of Terms:

All "line segments" are given.

Each line segment is defined by a cartesian pair of [x,y] for each of it's endpoints.

Initial Question statement about a topological condition states that:

detect which line segment lies on a circle (in figure) OR not?

Author's own comment adds:

find which segments not only intersect and also lies in the circle.

The Question Title states to:

pick up line segments which is inside or intersect a circle

From the Title & being confirmed by the comment it seems, that for meeting a topological condition for a "line segment" to "lie on a circle" it is enough, if any of it's endpoints { 1 | 2 } is inside the circle.

Should this topological assumption fails, the jez-posted vector-normal distance solution is correct.

Solution:

based on disambiguated terminology definition & topology condition, the set of "line segment"-s meeting the defined constraints is obtained by:

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 )
Community
  • 1
  • 1
user3666197
  • 1
  • 6
  • 50
  • 92
  • 1
    Simply beautiful answer. – Spider Nov 04 '14 at 16:52
  • 1
    There's one red line in your figure that this doesn't detect. It starts and ends outside, but passes through, the circle. – jez Nov 04 '14 at 18:04
  • @user3666197 . Are you sure that your code gives the same figure shown in the question? I am afraid that your code is not optimized for that. You can verify it by plotting – Spider Nov 04 '14 at 21:05
  • wrong answer, does not detect case when points are outside the circle, but line intersects the circle. – Chiefir Jan 03 '19 at 10:47