简体   繁体   English

如何有效计算环形空间中的距离?

[英]How to efficiently calculate distance in toroidal space?

I have game window of size 640 by 480 and it is populated by particles, but when a particle goes off to one side, it wraps around to the other (ie it is a toroid). 我的游戏窗口大小为640 x 480,并且其中填充有粒子,但是当粒子移到一侧时,它将环绕到另一侧(即,它是环形的)。

I want to calculate the distance between each particle, since this will be used to apply different forces to each particle. 我想计算每个粒子之间的距离,因为这将用于对每个粒子施加不同的力。

At first I looped through each pair of particles, and then rescaled everything so that the first particle in pair was centered and then calculated the distance to the second particle, but this was extremely slow to run. 首先,我遍历每对粒子,然后重新缩放所有内容,以使第一个粒子居中,然后计算到第二个粒子的距离,但是运行起来非常慢。

Then I found some functions in scipy.spatial.distance that allow me to calculate the distance between all points very quickly, but the only problem is that it doesn't take into account the wrap around. 然后,我在scipy.spatial.distance中找到了一些函数,这些函数可以让我非常快速地计算所有点之间的距离,但是唯一的问题是它没有考虑回绕。

Here is my current code 这是我当前的代码

from scipy.spatial.distance import pdist, squareform
...
distance = squareform(pdist([(p.x, p.y) for p in particles]))

This works for particles near the center, but if one particle is at (1, 320) and the other particle is at (639, 320), then it calculates their distance as 638 instead of 2. It doesn't take into account the wrap. 这适用于中心附近的粒子,但是如果一个粒子位于(1,320),而另一个粒子位于(639,320),则它将其距离计算为638而不是2。它没有考虑包裹。

Is there a different function I can use, or some transformation I can apply before/after to take into account the wrap? 我可以使用其他功能,还是可以在套用之前/之后套用某些转换,以将换行考虑在内?

You can compute the smaller of the x and y differences (the in-window difference versus the edge-crossing distance) like this: 您可以像这样计算x和y差异中的较小者(窗口内差异与边缘交叉距离):

game_width = 640
game_height = 480

def smaller_xy(point1, point2):

    xdiff = abs(point1.x - point2.x)
    if xdiff > (game_width / 2):
        xdiff = game_width - xdiff

    ydiff = abs(point1.y - point2.y)
    if ydiff > (game_height / 2):
        ydiff = game_height - ydiff

    return xdiff, ydiff

That is, if the in-window distance in the x or y directions is greater than half the size of the window in that direction, it's better to go off the edge -- and in that case the distance will be the window size in that direction minus the original in-window distance. 也就是说,如果在x或y方向上的窗口内距离大于该方向上的窗口大小的一半,则最好避开边缘-在这种情况下,距离将是该方向上的窗口大小方向减去原始窗口内距离。

Obviously, once you have the x and y separations you can compute the distance between the points as: 显然,一旦有了x和y间距,就可以计算点之间的距离为:

import math

small_x, small_y = smaller_xy(p1, p2)
least_distance = math.sqrt(small_x**2 + small_y**2)

However, depending on how your force calculation is defined, you might find that all you really need is the square of the distance (just ( small_x**2 + small_y**2 )) and therefore you can avoid the work of finding the sqrt . 但是,根据定义的力计算方式,您可能会发现,您真正需要的只是距离的平方(仅( small_x**2 + small_y**2 )),因此可以避免查找sqrt的工作。 。

To get this plumbed into scipy.pdist , note that pdist can be called with a function argument in addition to the points, as: 要将其放入scipy.pdist ,请注意,除了要点之外,还可以使用函数参数来调用pdist ,例如:

Y = pdist(X, func)

This is the last form of invocation shown in the description of pdist at https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html#scipy.spatial.distance.pdist 这是在https://docs.scipy.org/doc/scipy/reference/generation/scipy.spatial.distance.pdist.html#scipy.spatial.distance.pdistpdist说明中显示的最后一种调用形式

You should be able to use that feature to cause pdist to to build its distances-between-all-pairs-of-points matrix on the basis of distances calculated by a callback function that applies the smaller_xy computation. 您应该能够使用该功能,使pdist根据应用了smaller_xy计算的回调函数计算出的距离,建立其所pdist对之间的距离矩阵。

Let's imagine you replicate four boards above, below, left, and right to the original board, and also the particles on the original board onto the new boards. 假设您将四个板块复制到原始板块的上方,下方,左侧和右侧,并将原始板块上的粒子复制到新板上。 Let's also mark the particles. 让我们也标记粒子。

Denote the N particles on the original board o(i) , i running from 1 and N . 表示原始板o(i)上的N粒子, i1N运行。 a(i) for the particles on the replicated board above. 上面复制的板上的粒子的a(i) b(i) , l(i) , r(i) for below, left, and right respectively. b(i)l(i)r(i)分别位于下方,左侧和右侧。

For all distinct i and j , you need to find distances between o(i) and o(j) , o(i) and a(j) , etc, etc. There are 5x5 = 25 distances to compute for each pair of i and j . 对于所有不同的ij ,您需要找到o(i)o(j)o(i)a(j)等之间的距离,等等。对于每对i要计算5x5 = 25的距离和j Once you have all these distances, take the minimum for each pair, that's your distance for i and j . 一旦有了所有这些距离,就以每对中的最小值为准,这就是ij距离。

I was thinking there may be ways to prune the computation. 我当时想可能有一些方法可以简化计算。 But my thinking is you would need to at least compute distances between particles to the boarders and compare that with distances on original board. 但是我的想法是,您至少需要计算粒子与边界之间的距离,并将其与原始电路板上的距离进行比较。 That is an overhead as well. 这也是开销。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM