【问题标题】:Find new position for overlapping circles找到重叠圆圈的新位置
【发布时间】:2019-12-21 15:09:21
【问题描述】:

我正在尝试编写一个代码,对于给定的圆圈列表 (list1),它能够找到新圆圈 (list2) 的位置。 list1 和 list2 的长度相同,因为 list1 中的每个圆圈都必须有来自 list2 的圆圈。

  1. 每对圆(比如 list1 中的 circle1 和 list2 中的 circle2)必须尽可能靠近,
  2. list2 中的圆圈不能与 list1 中的圆圈重叠,而单个列表的圆圈可以相互重叠。

list1 是固定的,所以现在我必须从 list2 中找到圆圈的正确位置。

我写了这个简单的函数来识别两个圆圈是否重叠:

def overlap(x1, y1, x2, y2, r1, r2):
    distSq = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)
    radSumSq = (r1 + r2) * (r1 + r2)
    if (distSq >= radSumSq):
        return False # no overlap
    else:
        return True  #overlap

这是列表1:

与:

x=[14.11450195 14.14184093 14.15435028 14.16206741 14.16951752 14.17171097
 14.18569565 14.19700241 14.23129082 14.24083233 14.24290752 14.24968338
 14.2518959  14.26536751 14.27209759 14.27612877 14.2904377  14.29187012
 14.29409599 14.29618549 14.30615044 14.31624985 14.3206892  14.3228569
 14.36143875 14.36351967 14.36470699 14.36697292 14.37235737 14.41422081
 14.42583466 14.43226814 14.43319225 14.4437027  14.4557848  14.46592999
 14.47036076 14.47452068 14.47815609 14.52229309 14.53059006 14.53404236
 14.5411644 ] 
y=[-0.35319126 -0.44222349 -0.44763246 -0.35669261 -0.24366629 -0.3998799
 -0.38940558 -0.57744932 -0.45223859 -0.21021004 -0.44250247 -0.45866323
 -0.47203487 -0.51684451 -0.44884869 -0.2018993  -0.40296811 -0.23641759
 -0.18019417 -0.33391538 -0.53565156 -0.45215255 -0.40939832 -0.26936951
 -0.30894437 -0.55504167 -0.47177047 -0.45573688 -0.43100587 -0.5805912
 -0.21770373 -0.199422   -0.17372169 -0.38522363 -0.56950212 -0.56947368
 -0.48770753 -0.24940367 -0.31492445 -0.54263926 -0.53460872 -0.4053807
 -0.43733299]
radius = 0.014

复制和粘贴...

x = [14.11450195,14.14184093,14.15435028,14.16206741,14.16951752,
     14.17171097,14.18569565,14.19700241,14.23129082,14.24083233,
     14.24290752,14.24968338,14.2518959,14.26536751,14.27209759,
     14.27612877,14.2904377,14.29187012,14.29409599,14.29618549,
     14.30615044,14.31624985,14.3206892,14.3228569,14.36143875,
     14.36351967,14.36470699,14.36697292,14.37235737,14.41422081,
     14.42583466,14.43226814,14.43319225,14.4437027,14.4557848,
     14.46592999,14.47036076,14.47452068,14.47815609,14.52229309,
     14.53059006,14.53404236,14.5411644]

y = [-0.35319126,-0.44222349,-0.44763246,-0.35669261,-0.24366629,
     -0.3998799,-0.38940558,-0.57744932,-0.45223859,-0.21021004,
     -0.44250247,-0.45866323,-0.47203487,-0.51684451,-0.44884869,
     -0.2018993,-0.40296811,-0.23641759,-0.18019417,-0.33391538,
     -0.53565156,-0.45215255,-0.40939832,-0.26936951,-0.30894437,
     -0.55504167,-0.47177047,-0.45573688,-0.43100587,-0.5805912,
     -0.21770373,-0.199422,-0.17372169,-0.38522363,-0.56950212,
     -0.56947368,-0.48770753,-0.24940367,-0.31492445,-0.54263926,
     -0.53460872,-0.4053807,-0.43733299]

现在我不确定我必须做什么,我的第一个想法是绘制 list2 的圆圈,从列表一中获取 x 和 y,然后执行 x+cy+c 之类的操作,其中 c 是固定的价值。然后我可以调用我的重叠函数,如果有重叠,我可以增加c 的值。 这样我就有了 2 个 for 循环。现在,我的问题是:

  1. 有办法避免for 循环吗?
  2. 是否有一种智能的解决方案可以为 list1 中的每个圆圈(不与 list2 中的其他圆圈重叠)找到一个邻居(来自 list2 的圆圈)?

【问题讨论】:

  • a way to avoid for loops? - 你无法避免迭代,但如果你使用 numpy ndarrays,你可以将迭代推送到 c 代码中,这样会更快。
  • 是的,就像一些来自 scipy 的最近邻,但我不知道这是否有什么...
  • 您的实际数据集有多大?两个列表中有多少个圆圈?使用小型数据集一次处理一个方面,然后在一切正常时进行优化。似乎您可能需要为每个重叠的圆圈保存最近的邻居信息,然后使用组的几何形状来计算 safe 向量来移动有问题的圆圈,这样您就不会迭代随机向量并拥有再次迭代。您可能需要对这些组的容器进行创意..
  • 你能提供一个list2的例子吗?
  • 在我的主题中有这个答案。您已经有了一个带有 xy 坐标的 list1 示例。样本list2list1 相似,圆数相同,半径相同。真正的数据集更大,这就是为什么我要优化流程以避免for 循环。

标签: python geometry overlap


【解决方案1】:

使用 numpy 数组,可以避免 for 循环。

根据您的示例进行设置。

import numpy as np

#Using your x and y
c1 = np.array([x,y]).T

# random set of other centers within the same range as c1
c2 = np.random.random((10,2))
np.multiply(c2, c1.max(0)-c1.min(0),out = c2)
np.add(c2, c1.min(0), out=c2)

radius = 0.014
r = radius
min_d = (2*r)*(2*r)

plot_circles(c1,c2)    # see function at end

c1 中的每个中心到c2 中的每个中心的距离数组

def dist(c1,c2):
    dx = c1[:,0,None] - c2[:,0]
    dy = c1[:,1,None] - c2[:,1]
    return dx*dx + dy*dy

d = dist(c1,c2)

或者你可以使用 scipy.spatial

from scipy.spatial import distance
d = distance.cdist(c1,c2,'sqeuclidean')

为相交的圆创建一个二维布尔数组。

intersect = d <= min_d

从两组中找出重叠圆的索引。

a,b = np.where(intersect)
plot_circles(c1[a],c2[b])

使用 intersectab 来索引 c1、c2 和 d,您应该能够得到相交的圆组,然后弄清楚如何移动 c2 中心 - 但我将把它留给另一个问题/答案。如果list2 圆与list1 圆相交 - 找到两者之间的线并沿该线移动。如果list2 圆与多个list1 圆相交 - 沿着垂直于该线的线找到两个closestlist1circles and move thelitst2` 圆之间的线。你没有提到移动圆圈的任何限制,所以也许随机移动然后再次找到相交,但这可能是有问题的。在下图中,弄清楚如何移动大部分红色圆圈可能很简单,但蓝色圆圈中的组可能需要不同的策略。

以下是获取组的一些示例:

>>> for f,g,h in zip(c1[a],c2[b],d[a,b]):
    print(f,g,h)
>>> c1[intersect.any(1)],c2[intersect.any(0)]
>>> for (f,g) in zip(c2,intersect.T):
    if g.any():
            print(f.tolist(),c1[g].tolist())

import matplotlib as mpl
from matplotlib import pyplot as plt

def plot_circles(c1,c2):

    bounds = np.array([c1.min(0),c2.min(0),c1.max(0),c2.max(0)])
    xmin, ymin = bounds.min(0)
    xmax, ymax = bounds.max(0)

    circles1 = [mpl.patches.Circle(xy,radius=r,fill=False,edgecolor='g') for xy in c1]
    circles2 = [mpl.patches.Circle(xy,radius=r,fill=False,edgecolor='r') for xy in c2]
    fig = plt.figure()
    ax = fig.add_subplot(111)
    for c in circles2:
        ax.add_artist(c)
    for c in circles1:
        ax.add_artist(c)

    ax.set_xlim(xmin-r,xmax+r)
    ax.set_ylim(ymin-r,ymax+r)

    plt.show()
    plt.close()

【讨论】: