Reputation: 1119
I have n lines and m circles.
I have a [n,2] numpy array of line start points:
[x1_1,y1_1],
[x1_2,y1_2],
...
[x1_n,y1_n]
And a [n,2] numpy array of line end points:
[x2_1,y2_1],
[x2_2,y2_2],
...
[x2_n,y2_n]
And an [m,2] numpy array of circle centers:
[cx_1,cy_1],
...
[cx_m,cy_m]
And an [m,1] numpy array of circle radii:
[cr_1...cr_m]
I would like to efficiently get an [n,m] numpy array where array[i,j] is True if line i intersects circle j.
In general I would take the normalised perpendicular vector to each line and take the dot product of that with each (xi,yi) - (cx_j,cy_y) and ask if it's less than cr_i; but I also have to check whether that implied point is on the line and check each end individually if not. I'm wondering if there is a more elegant solution.
Upvotes: 0
Views: 301
Reputation: 7863
I can't think of a substantially simpler algorithm than the one outlined in the question. As far as the implementation is concerned, it is easy to make these computations using shapely
.
First, lets generate and plot some sample data:
from itertools import product
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import matplotlib.colors as mcolors
from shapely.geometry import LineString, Point, MultiLineString
import numpy as np
import pandas as pd
# generate data
rng = np.random.default_rng(123)
start = (rng.random((3, 2)) - .5) * 5
end = (rng.random((3, 2)) - .5) * 5
center = (rng.random((4, 2)) - .5) * 5
radius = rng.random((4, 1)) * 3
# plot lines and circles
fig, ax = plt.subplots()
fig.set_size_inches(8, 8)
ax.set_aspect('equal')
colors = list(mcolors.TABLEAU_COLORS.keys())
for i, ends in enumerate(zip(start, end)):
ax.plot(*zip(*ends), label=f"line {i}")
for i, (c, r) in enumerate(zip(center, radius)):
ax.add_patch(Circle(c, r, fill=False, ec=colors[i], label=f"circle {i}"))
plt.legend()
plt.show()
This gives:
Next, compute the array of intersections, with rows corresponding to lines and columns corresponding to circles:
lines = [LineString(ends) for ends in list(zip(start, end))]
circles = [Point(c).buffer(r).boundary for c, r in zip(center, radius)]
out = np.empty((len(lines), len(circles)), dtype=bool)
for i, (l, c) in enumerate(product(lines, circles)):
out[np.unravel_index(i, out.shape)] = l.intersects(c)
#convert to a dataframe for better display
df = pd.DataFrame(out)
df.index.name = 'lines'
df.columns.name = 'circles'
print(df)
The result:
circles 0 1 2 3
lines
0 True False True True
1 False False False True
2 False False False False
Upvotes: 1
Reputation: 2691
Ok, assume to have these shape
start = (np.random.random((3,2))-.5)*5
end = (np.random.random((3,2))-.5)*5
center = (np.random.random((4,2))-.5)*5
radius = np.random.random((4,1))*3
for each center we can compute the distance from the three lines by:
D = np.array([
np.linalg.norm(np.cross(end-start, start-c).reshape(-1,1),axis=1)/np.linalg.norm((end-start).reshape(-1,2), axis=1)
for c in center
]).T
D[i,j] will be the distance between line i (in rows) and center j (in clumns). Now we can simply compare this distances to the radius distances with:
I = (d<radius.repeat(len(start), axis=1).T)
I is a matrix of the same shape of D; I[i,j] is True if the distance between the line i and the center j is lower than the radius j (and so if the line i intersect the circle j) and False otherwise.
I know it is not very elegant, but I hope it can be useful.
Upvotes: 1